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 if (path_
.get() || wasExtant_
||
244 (status_
&& *status_
== OpenStatus::Scratch
)) {
245 unit().OpenUnit(status_
, action_
, position_
.value_or(Position::AsIs
),
246 std::move(path_
), pathLength_
, convert_
, *this);
248 unit().OpenAnonymousUnit(
249 status_
, action_
, position_
.value_or(Position::AsIs
), convert_
, *this);
252 if (*access_
!= unit().access
) {
254 SignalError("ACCESS= may not be changed on an open unit");
259 unit().access
= *access_
;
262 if (!unit().isUnformatted
) {
263 unit().isUnformatted
= isUnformatted_
;
265 if (isUnformatted_
&& *isUnformatted_
!= *unit().isUnformatted
) {
267 SignalError("FORM= may not be changed on an open unit");
269 unit().isUnformatted
= *isUnformatted_
;
271 if (!unit().isUnformatted
) {
272 // Set default format (C.7.4 point 2).
273 unit().isUnformatted
= unit().access
!= Access::Sequential
;
275 if (!wasExtant_
&& InError()) {
276 // Release the new unit on failure
277 unit().CloseUnit(CloseStatus::Delete
, *this);
278 unit().DestroyClosed();
280 IoStatementBase::CompleteOperation();
283 int OpenStatementState::EndIoStatement() {
285 return ExternalIoStatementBase::EndIoStatement();
288 int CloseStatementState::EndIoStatement() {
290 int result
{ExternalIoStatementBase::EndIoStatement()};
291 unit().CloseUnit(status_
, *this);
292 unit().DestroyClosed();
296 void NoUnitIoStatementState::CompleteOperation() {
297 SignalPendingError();
298 IoStatementBase::CompleteOperation();
301 int NoUnitIoStatementState::EndIoStatement() {
303 auto result
{IoStatementBase::EndIoStatement()};
308 template <Direction
DIR>
309 ExternalIoStatementState
<DIR>::ExternalIoStatementState(
310 ExternalFileUnit
&unit
, const char *sourceFile
, int sourceLine
)
311 : ExternalIoStatementBase
{unit
, sourceFile
, sourceLine
}, mutableModes_
{
313 if constexpr (DIR == Direction::Output
) {
314 // If the last statement was a non-advancing IO input statement, the unit
315 // furthestPositionInRecord was not advanced, but the positionInRecord may
316 // have been advanced. Advance furthestPositionInRecord here to avoid
317 // overwriting the part of the record that has been read with blanks.
318 unit
.furthestPositionInRecord
=
319 std::max(unit
.furthestPositionInRecord
, unit
.positionInRecord
);
323 template <Direction
DIR>
324 void ExternalIoStatementState
<DIR>::CompleteOperation() {
325 if (completedOperation()) {
328 if constexpr (DIR == Direction::Input
) {
329 BeginReadingRecord(); // in case there were no I/O items
330 if (mutableModes().nonAdvancing
&& !InError()) {
331 unit().leftTabLimit
= unit().furthestPositionInRecord
;
333 FinishReadingRecord();
336 if (mutableModes().nonAdvancing
) {
337 // Make effects of positioning past the last Emit() visible with blanks.
338 std::int64_t n
{unit().positionInRecord
- unit().furthestPositionInRecord
};
339 while (n
-- > 0 && unit().Emit(" ", 1, 1, *this)) {
341 unit().leftTabLimit
= unit().positionInRecord
;
343 unit().AdvanceRecord(*this);
345 unit().FlushIfTerminal(*this);
347 return IoStatementBase::CompleteOperation();
350 template <Direction
DIR> int ExternalIoStatementState
<DIR>::EndIoStatement() {
352 return ExternalIoStatementBase::EndIoStatement();
355 template <Direction
DIR>
356 bool ExternalIoStatementState
<DIR>::Emit(
357 const char *data
, std::size_t bytes
, std::size_t elementBytes
) {
358 if constexpr (DIR == Direction::Input
) {
359 Crash("ExternalIoStatementState::Emit(char) called for input statement");
361 return unit().Emit(data
, bytes
, elementBytes
, *this);
364 template <Direction
DIR>
365 std::size_t ExternalIoStatementState
<DIR>::GetNextInputBytes(const char *&p
) {
366 return unit().GetNextInputBytes(p
, *this);
369 template <Direction
DIR>
370 bool ExternalIoStatementState
<DIR>::AdvanceRecord(int n
) {
372 if (!unit().AdvanceRecord(*this)) {
379 template <Direction
DIR> void ExternalIoStatementState
<DIR>::BackspaceRecord() {
380 unit().BackspaceRecord(*this);
383 template <Direction
DIR>
384 void ExternalIoStatementState
<DIR>::HandleAbsolutePosition(std::int64_t n
) {
385 return unit().HandleAbsolutePosition(n
);
388 template <Direction
DIR>
389 void ExternalIoStatementState
<DIR>::HandleRelativePosition(std::int64_t n
) {
390 return unit().HandleRelativePosition(n
);
393 template <Direction
DIR>
394 bool ExternalIoStatementState
<DIR>::BeginReadingRecord() {
395 if constexpr (DIR == Direction::Input
) {
396 return unit().BeginReadingRecord(*this);
398 Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "
404 template <Direction
DIR>
405 void ExternalIoStatementState
<DIR>::FinishReadingRecord() {
406 if constexpr (DIR == Direction::Input
) {
407 unit().FinishReadingRecord(*this);
409 Crash("ExternalIoStatementState<Direction::Output>::FinishReadingRecord() "
414 template <Direction
DIR, typename CHAR
>
415 ExternalFormattedIoStatementState
<DIR, CHAR
>::ExternalFormattedIoStatementState(
416 ExternalFileUnit
&unit
, const CHAR
*format
, std::size_t formatLength
,
417 const Descriptor
*formatDescriptor
, const char *sourceFile
, int sourceLine
)
418 : ExternalIoStatementState
<DIR>{unit
, sourceFile
, sourceLine
},
419 format_
{*this, format
, formatLength
, formatDescriptor
} {}
421 template <Direction
DIR, typename CHAR
>
422 void ExternalFormattedIoStatementState
<DIR, CHAR
>::CompleteOperation() {
423 if (this->completedOperation()) {
426 if constexpr (DIR == Direction::Input
) {
427 this->BeginReadingRecord(); // in case there were no I/O items
429 format_
.Finish(*this);
430 return ExternalIoStatementState
<DIR>::CompleteOperation();
433 template <Direction
DIR, typename CHAR
>
434 int ExternalFormattedIoStatementState
<DIR, CHAR
>::EndIoStatement() {
436 return ExternalIoStatementState
<DIR>::EndIoStatement();
439 std::optional
<DataEdit
> IoStatementState::GetNextDataEdit(int n
) {
440 return common::visit(
441 [&](auto &x
) { return x
.get().GetNextDataEdit(*this, n
); }, u_
);
444 bool IoStatementState::Emit(
445 const char *data
, std::size_t bytes
, std::size_t elementBytes
) {
446 return common::visit(
447 [=](auto &x
) { return x
.get().Emit(data
, bytes
, elementBytes
); }, u_
);
450 bool IoStatementState::Receive(
451 char *data
, std::size_t n
, std::size_t elementBytes
) {
452 return common::visit(
453 [=](auto &x
) { return x
.get().Receive(data
, n
, elementBytes
); }, u_
);
456 std::size_t IoStatementState::GetNextInputBytes(const char *&p
) {
457 return common::visit(
458 [&](auto &x
) { return x
.get().GetNextInputBytes(p
); }, u_
);
461 bool IoStatementState::AdvanceRecord(int n
) {
462 return common::visit([=](auto &x
) { return x
.get().AdvanceRecord(n
); }, u_
);
465 void IoStatementState::BackspaceRecord() {
466 common::visit([](auto &x
) { x
.get().BackspaceRecord(); }, u_
);
469 void IoStatementState::HandleRelativePosition(std::int64_t n
) {
470 common::visit([=](auto &x
) { x
.get().HandleRelativePosition(n
); }, u_
);
473 void IoStatementState::HandleAbsolutePosition(std::int64_t n
) {
474 common::visit([=](auto &x
) { x
.get().HandleAbsolutePosition(n
); }, u_
);
477 void IoStatementState::CompleteOperation() {
478 common::visit([](auto &x
) { x
.get().CompleteOperation(); }, u_
);
481 int IoStatementState::EndIoStatement() {
482 return common::visit([](auto &x
) { return x
.get().EndIoStatement(); }, u_
);
485 ConnectionState
&IoStatementState::GetConnectionState() {
486 return common::visit(
487 [](auto &x
) -> ConnectionState
& { return x
.get().GetConnectionState(); },
491 MutableModes
&IoStatementState::mutableModes() {
492 return common::visit(
493 [](auto &x
) -> MutableModes
& { return x
.get().mutableModes(); }, u_
);
496 bool IoStatementState::BeginReadingRecord() {
497 return common::visit(
498 [](auto &x
) { return x
.get().BeginReadingRecord(); }, u_
);
501 IoErrorHandler
&IoStatementState::GetIoErrorHandler() const {
502 return common::visit(
503 [](auto &x
) -> IoErrorHandler
& {
504 return static_cast<IoErrorHandler
&>(x
.get());
509 ExternalFileUnit
*IoStatementState::GetExternalFileUnit() const {
510 return common::visit(
511 [](auto &x
) { return x
.get().GetExternalFileUnit(); }, u_
);
514 std::optional
<char32_t
> IoStatementState::GetCurrentChar(
515 std::size_t &byteCount
) {
516 const char *p
{nullptr};
517 std::size_t bytes
{GetNextInputBytes(p
)};
522 const ConnectionState
&connection
{GetConnectionState()};
523 if (connection
.isUTF8
) {
524 std::size_t length
{MeasureUTF8Bytes(*p
)};
525 if (length
<= bytes
) {
526 if (auto result
{DecodeUTF8(p
)}) {
531 GetIoErrorHandler().SignalError(IostatUTF8Decoding
);
532 // Error recovery: return the next byte
533 } else if (connection
.internalIoCharKind
> 1) {
534 byteCount
= connection
.internalIoCharKind
;
535 if (byteCount
== 2) {
536 return *reinterpret_cast<const char16_t
*>(p
);
538 return *reinterpret_cast<const char32_t
*>(p
);
546 std::optional
<char32_t
> IoStatementState::NextInField(
547 std::optional
<int> &remaining
, const DataEdit
&edit
) {
548 std::size_t byteCount
{0};
549 if (!remaining
) { // Stream, list-directed, or NAMELIST
550 if (auto next
{GetCurrentChar(byteCount
)}) {
551 if (edit
.IsListDirected()) {
552 // list-directed or NAMELIST: check for separators
562 case '\n': // for stream access
565 if (!(edit
.modes
.editingFlags
& decimalComma
)) {
570 if (edit
.modes
.editingFlags
& decimalComma
) {
578 HandleRelativePosition(byteCount
);
582 } else if (*remaining
> 0) {
583 if (auto next
{GetCurrentChar(byteCount
)}) {
584 if (byteCount
> static_cast<std::size_t>(*remaining
)) {
587 *remaining
-= byteCount
;
588 HandleRelativePosition(byteCount
);
592 if (CheckForEndOfRecord()) { // do padding
594 return std::optional
<char32_t
>{' '};
600 bool IoStatementState::CheckForEndOfRecord() {
601 const ConnectionState
&connection
{GetConnectionState()};
602 if (!connection
.IsAtEOF()) {
603 if (auto length
{connection
.EffectiveRecordLength()}) {
604 if (connection
.positionInRecord
>= *length
) {
605 IoErrorHandler
&handler
{GetIoErrorHandler()};
606 const auto &modes
{mutableModes()};
607 if (modes
.nonAdvancing
) {
608 if (connection
.access
== Access::Stream
&&
609 connection
.unterminatedRecord
) {
610 // Reading final unterminated record left by a
611 // non-advancing WRITE on a stream file prior to
612 // positioning or ENDFILE.
617 } else if (!modes
.pad
) {
618 handler
.SignalError(IostatRecordReadOverrun
);
620 return modes
.pad
; // PAD='YES'
627 bool IoStatementState::Inquire(
628 InquiryKeywordHash inquiry
, char *out
, std::size_t chars
) {
629 return common::visit(
630 [&](auto &x
) { return x
.get().Inquire(inquiry
, out
, chars
); }, u_
);
633 bool IoStatementState::Inquire(InquiryKeywordHash inquiry
, bool &out
) {
634 return common::visit(
635 [&](auto &x
) { return x
.get().Inquire(inquiry
, out
); }, u_
);
638 bool IoStatementState::Inquire(
639 InquiryKeywordHash inquiry
, std::int64_t id
, bool &out
) {
640 return common::visit(
641 [&](auto &x
) { return x
.get().Inquire(inquiry
, id
, out
); }, u_
);
644 bool IoStatementState::Inquire(InquiryKeywordHash inquiry
, std::int64_t &n
) {
645 return common::visit(
646 [&](auto &x
) { return x
.get().Inquire(inquiry
, n
); }, u_
);
649 std::int64_t IoStatementState::InquirePos() {
650 return common::visit([&](auto &x
) { return x
.get().InquirePos(); }, u_
);
653 void IoStatementState::GotChar(int n
) {
654 if (auto *formattedIn
{
655 get_if
<FormattedIoStatementState
<Direction::Input
>>()}) {
656 formattedIn
->GotChar(n
);
658 GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
659 "statement that is not formatted input");
664 FormattedIoStatementState
<Direction::Input
>::GetEditDescriptorChars() const {
668 void FormattedIoStatementState
<Direction::Input
>::GotChar(int n
) {
672 bool ListDirectedStatementState
<Direction::Output
>::EmitLeadingSpaceOrAdvance(
673 IoStatementState
&io
, std::size_t length
, bool isCharacter
) {
677 const ConnectionState
&connection
{io
.GetConnectionState()};
678 int space
{connection
.positionInRecord
== 0 ||
679 !(isCharacter
&& lastWasUndelimitedCharacter())};
680 set_lastWasUndelimitedCharacter(false);
681 if (connection
.NeedAdvance(space
+ length
)) {
682 return io
.AdvanceRecord();
685 return EmitAscii(io
, " ", 1);
690 std::optional
<DataEdit
>
691 ListDirectedStatementState
<Direction::Output
>::GetNextDataEdit(
692 IoStatementState
&io
, int maxRepeat
) {
694 edit
.descriptor
= DataEdit::ListDirected
;
695 edit
.repeat
= maxRepeat
;
696 edit
.modes
= io
.mutableModes();
700 std::optional
<DataEdit
>
701 ListDirectedStatementState
<Direction::Input
>::GetNextDataEdit(
702 IoStatementState
&io
, int maxRepeat
) {
703 // N.B. list-directed transfers cannot be nonadvancing (C1221)
704 ConnectionState
&connection
{io
.GetConnectionState()};
706 edit
.descriptor
= DataEdit::ListDirected
;
707 edit
.repeat
= 1; // may be overridden below
708 edit
.modes
= io
.mutableModes();
709 if (hitSlash_
) { // everything after '/' is nullified
710 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
714 if (edit
.modes
.editingFlags
& decimalComma
) {
717 std::size_t byteCount
{0};
718 if (remaining_
> 0 && !realPart_
) { // "r*c" repetition in progress
719 RUNTIME_CHECK(io
.GetIoErrorHandler(), repeatPosition_
.has_value());
720 repeatPosition_
.reset(); // restores the saved position
721 if (!imaginaryPart_
) {
722 edit
.repeat
= std::min
<int>(remaining_
, maxRepeat
);
723 auto ch
{io
.GetCurrentChar(byteCount
)};
724 if (!ch
|| *ch
== ' ' || *ch
== '\t' || *ch
== comma
) {
725 // "r*" repeated null
726 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
729 remaining_
-= edit
.repeat
;
730 if (remaining_
> 0) {
731 repeatPosition_
.emplace(io
);
733 if (!imaginaryPart_
) {
737 // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018
738 if (imaginaryPart_
) {
739 imaginaryPart_
= false;
740 } else if (realPart_
) {
742 imaginaryPart_
= true;
743 edit
.descriptor
= DataEdit::ListDirectedImaginaryPart
;
745 auto ch
{io
.GetNextNonBlank(byteCount
)};
746 if (ch
&& *ch
== comma
&& eatComma_
) {
747 // Consume comma & whitespace after previous item.
748 // This includes the comma between real and imaginary components
749 // in list-directed/NAMELIST complex input.
750 // (When DECIMAL='COMMA', the comma is actually a semicolon.)
751 io
.HandleRelativePosition(byteCount
);
752 ch
= io
.GetNextNonBlank(byteCount
);
760 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
763 if (*ch
== comma
) { // separator: null value
764 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
767 if (imaginaryPart_
) { // can't repeat components
770 if (*ch
>= '0' && *ch
<= '9') { // look for "r*" repetition count
771 auto start
{connection
.positionInRecord
};
774 static auto constexpr clamp
{(std::numeric_limits
<int>::max() - '9') / 10};
779 r
= 10 * r
+ (*ch
- '0');
780 io
.HandleRelativePosition(byteCount
);
781 ch
= io
.GetCurrentChar(byteCount
);
782 } while (ch
&& *ch
>= '0' && *ch
<= '9');
783 if (r
> 0 && ch
&& *ch
== '*') { // subtle: r must be nonzero
784 io
.HandleRelativePosition(byteCount
);
785 ch
= io
.GetCurrentChar(byteCount
);
786 if (ch
&& *ch
== '/') { // r*/
788 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
791 if (!ch
|| *ch
== ' ' || *ch
== '\t' || *ch
== comma
) { // "r*" null
792 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
794 edit
.repeat
= std::min
<int>(r
, maxRepeat
);
795 remaining_
= r
- edit
.repeat
;
796 if (remaining_
> 0) {
797 repeatPosition_
.emplace(io
);
799 } else { // not a repetition count, just an integer value; rewind
800 connection
.positionInRecord
= start
;
803 if (!imaginaryPart_
&& ch
&& *ch
== '(') {
805 io
.HandleRelativePosition(byteCount
);
806 edit
.descriptor
= DataEdit::ListDirectedRealPart
;
811 template <Direction
DIR>
812 bool ExternalUnformattedIoStatementState
<DIR>::Receive(
813 char *data
, std::size_t bytes
, std::size_t elementBytes
) {
814 if constexpr (DIR == Direction::Output
) {
815 this->Crash("ExternalUnformattedIoStatementState::Receive() called for "
818 return this->unit().Receive(data
, bytes
, elementBytes
, *this);
821 template <Direction
DIR>
822 ChildIoStatementState
<DIR>::ChildIoStatementState(
823 ChildIo
&child
, const char *sourceFile
, int sourceLine
)
824 : IoStatementBase
{sourceFile
, sourceLine
}, child_
{child
} {}
826 template <Direction
DIR>
827 MutableModes
&ChildIoStatementState
<DIR>::mutableModes() {
828 return child_
.parent().mutableModes();
831 template <Direction
DIR>
832 ConnectionState
&ChildIoStatementState
<DIR>::GetConnectionState() {
833 return child_
.parent().GetConnectionState();
836 template <Direction
DIR>
837 ExternalFileUnit
*ChildIoStatementState
<DIR>::GetExternalFileUnit() const {
838 return child_
.parent().GetExternalFileUnit();
841 template <Direction
DIR> int ChildIoStatementState
<DIR>::EndIoStatement() {
843 auto result
{IoStatementBase::EndIoStatement()};
844 child_
.EndIoStatement(); // annihilates *this in child_.u_
848 template <Direction
DIR>
849 bool ChildIoStatementState
<DIR>::Emit(
850 const char *data
, std::size_t bytes
, std::size_t elementBytes
) {
851 return child_
.parent().Emit(data
, bytes
, elementBytes
);
854 template <Direction
DIR>
855 std::size_t ChildIoStatementState
<DIR>::GetNextInputBytes(const char *&p
) {
856 return child_
.parent().GetNextInputBytes(p
);
859 template <Direction
DIR>
860 void ChildIoStatementState
<DIR>::HandleAbsolutePosition(std::int64_t n
) {
861 return child_
.parent().HandleAbsolutePosition(n
);
864 template <Direction
DIR>
865 void ChildIoStatementState
<DIR>::HandleRelativePosition(std::int64_t n
) {
866 return child_
.parent().HandleRelativePosition(n
);
869 template <Direction
DIR, typename CHAR
>
870 ChildFormattedIoStatementState
<DIR, CHAR
>::ChildFormattedIoStatementState(
871 ChildIo
&child
, const CHAR
*format
, std::size_t formatLength
,
872 const Descriptor
*formatDescriptor
, const char *sourceFile
, int sourceLine
)
873 : ChildIoStatementState
<DIR>{child
, sourceFile
, sourceLine
},
874 mutableModes_
{child
.parent().mutableModes()}, format_
{*this, format
,
878 template <Direction
DIR, typename CHAR
>
879 void ChildFormattedIoStatementState
<DIR, CHAR
>::CompleteOperation() {
880 if (!this->completedOperation()) {
881 format_
.Finish(*this);
882 ChildIoStatementState
<DIR>::CompleteOperation();
886 template <Direction
DIR, typename CHAR
>
887 int ChildFormattedIoStatementState
<DIR, CHAR
>::EndIoStatement() {
889 return ChildIoStatementState
<DIR>::EndIoStatement();
892 template <Direction
DIR, typename CHAR
>
893 bool ChildFormattedIoStatementState
<DIR, CHAR
>::AdvanceRecord(int n
) {
894 return this->child().parent().AdvanceRecord(n
);
897 template <Direction
DIR>
898 bool ChildUnformattedIoStatementState
<DIR>::Receive(
899 char *data
, std::size_t bytes
, std::size_t elementBytes
) {
900 return this->child().parent().Receive(data
, bytes
, elementBytes
);
903 template class InternalIoStatementState
<Direction::Output
>;
904 template class InternalIoStatementState
<Direction::Input
>;
905 template class InternalFormattedIoStatementState
<Direction::Output
>;
906 template class InternalFormattedIoStatementState
<Direction::Input
>;
907 template class InternalListIoStatementState
<Direction::Output
>;
908 template class InternalListIoStatementState
<Direction::Input
>;
909 template class ExternalIoStatementState
<Direction::Output
>;
910 template class ExternalIoStatementState
<Direction::Input
>;
911 template class ExternalFormattedIoStatementState
<Direction::Output
>;
912 template class ExternalFormattedIoStatementState
<Direction::Input
>;
913 template class ExternalListIoStatementState
<Direction::Output
>;
914 template class ExternalListIoStatementState
<Direction::Input
>;
915 template class ExternalUnformattedIoStatementState
<Direction::Output
>;
916 template class ExternalUnformattedIoStatementState
<Direction::Input
>;
917 template class ChildIoStatementState
<Direction::Output
>;
918 template class ChildIoStatementState
<Direction::Input
>;
919 template class ChildFormattedIoStatementState
<Direction::Output
>;
920 template class ChildFormattedIoStatementState
<Direction::Input
>;
921 template class ChildListIoStatementState
<Direction::Output
>;
922 template class ChildListIoStatementState
<Direction::Input
>;
923 template class ChildUnformattedIoStatementState
<Direction::Output
>;
924 template class ChildUnformattedIoStatementState
<Direction::Input
>;
926 void ExternalMiscIoStatementState::CompleteOperation() {
927 if (completedOperation()) {
930 ExternalFileUnit
&ext
{unit()};
933 ext
.FlushOutput(*this);
934 std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
937 ext
.BackspaceRecord(*this);
946 break; // handled in io-api.cpp BeginWait
948 return IoStatementBase::CompleteOperation();
951 int ExternalMiscIoStatementState::EndIoStatement() {
953 return ExternalIoStatementBase::EndIoStatement();
956 InquireUnitState::InquireUnitState(
957 ExternalFileUnit
&unit
, const char *sourceFile
, int sourceLine
)
958 : ExternalIoStatementBase
{unit
, sourceFile
, sourceLine
} {}
960 bool InquireUnitState::Inquire(
961 InquiryKeywordHash inquiry
, char *result
, std::size_t length
) {
962 if (unit().createdForInternalChildIo()) {
963 SignalError(IostatInquireInternalUnit
,
964 "INQUIRE of unit created for defined derived type I/O of an internal "
968 const char *str
{nullptr};
970 case HashInquiryKeyword("ACCESS"):
971 if (!unit().IsConnected()) {
974 switch (unit().access
) {
975 case Access::Sequential
:
987 case HashInquiryKeyword("ACTION"):
988 str
= !unit().IsConnected() ? "UNDEFINED"
989 : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE"
992 case HashInquiryKeyword("ASYNCHRONOUS"):
993 str
= !unit().IsConnected() ? "UNDEFINED"
994 : unit().mayAsynchronous() ? "YES"
997 case HashInquiryKeyword("BLANK"):
998 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1000 : mutableModes().editingFlags
& blankZero
? "ZERO"
1003 case HashInquiryKeyword("CARRIAGECONTROL"):
1006 case HashInquiryKeyword("CONVERT"):
1007 str
= unit().swapEndianness() ? "SWAP" : "NATIVE";
1009 case HashInquiryKeyword("DECIMAL"):
1010 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1012 : mutableModes().editingFlags
& decimalComma
? "COMMA"
1015 case HashInquiryKeyword("DELIM"):
1016 if (!unit().IsConnected() || unit().isUnformatted
.value_or(true)) {
1019 switch (mutableModes().delim
) {
1032 case HashInquiryKeyword("DIRECT"):
1033 str
= !unit().IsConnected() ? "UNKNOWN"
1034 : unit().access
== Access::Direct
||
1035 (unit().mayPosition() && unit().openRecl
)
1039 case HashInquiryKeyword("ENCODING"):
1040 str
= !unit().IsConnected() ? "UNKNOWN"
1041 : unit().isUnformatted
.value_or(true) ? "UNDEFINED"
1042 : unit().isUTF8
? "UTF-8"
1045 case HashInquiryKeyword("FORM"):
1046 str
= !unit().IsConnected() || !unit().isUnformatted
? "UNDEFINED"
1047 : *unit().isUnformatted
? "UNFORMATTED"
1050 case HashInquiryKeyword("FORMATTED"):
1051 str
= !unit().IsConnected() ? "UNDEFINED"
1052 : !unit().isUnformatted
? "UNKNOWN"
1053 : *unit().isUnformatted
? "NO"
1056 case HashInquiryKeyword("NAME"):
1057 str
= unit().path();
1059 return true; // result is undefined
1062 case HashInquiryKeyword("PAD"):
1063 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1065 : mutableModes().pad
? "YES"
1068 case HashInquiryKeyword("POSITION"):
1069 if (!unit().IsConnected() || unit().access
== Access::Direct
) {
1072 switch (unit().InquirePosition()) {
1073 case Position::Rewind
:
1076 case Position::Append
:
1079 case Position::AsIs
:
1085 case HashInquiryKeyword("READ"):
1086 str
= !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO";
1088 case HashInquiryKeyword("READWRITE"):
1089 str
= !unit().IsConnected() ? "UNDEFINED"
1090 : unit().mayRead() && unit().mayWrite() ? "YES"
1093 case HashInquiryKeyword("ROUND"):
1094 if (!unit().IsConnected() || unit().isUnformatted
.value_or(true)) {
1097 switch (mutableModes().round
) {
1098 case decimal::FortranRounding::RoundNearest
:
1101 case decimal::FortranRounding::RoundUp
:
1104 case decimal::FortranRounding::RoundDown
:
1107 case decimal::FortranRounding::RoundToZero
:
1110 case decimal::FortranRounding::RoundCompatible
:
1116 case HashInquiryKeyword("SEQUENTIAL"):
1117 // "NO" for Direct, since Sequential would not work if
1118 // the unit were reopened without RECL=.
1119 str
= !unit().IsConnected() ? "UNKNOWN"
1120 : unit().access
== Access::Sequential
? "YES"
1123 case HashInquiryKeyword("SIGN"):
1124 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1126 : mutableModes().editingFlags
& signPlus
? "PLUS"
1129 case HashInquiryKeyword("STREAM"):
1130 str
= !unit().IsConnected() ? "UNKNOWN"
1131 : unit().access
== Access::Stream
? "YES"
1134 case HashInquiryKeyword("UNFORMATTED"):
1135 str
= !unit().IsConnected() || !unit().isUnformatted
? "UNKNOWN"
1136 : *unit().isUnformatted
? "YES"
1139 case HashInquiryKeyword("WRITE"):
1140 str
= !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO";
1144 ToFortranDefaultCharacter(result
, length
, str
);
1147 BadInquiryKeywordHashCrash(inquiry
);
1152 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry
, bool &result
) {
1154 case HashInquiryKeyword("EXIST"):
1157 case HashInquiryKeyword("NAMED"):
1158 result
= unit().path() != nullptr;
1160 case HashInquiryKeyword("OPENED"):
1161 result
= unit().IsConnected();
1163 case HashInquiryKeyword("PENDING"):
1164 result
= false; // asynchronous I/O is not implemented
1167 BadInquiryKeywordHashCrash(inquiry
);
1172 bool InquireUnitState::Inquire(
1173 InquiryKeywordHash inquiry
, std::int64_t, bool &result
) {
1175 case HashInquiryKeyword("PENDING"):
1176 result
= false; // asynchronous I/O is not implemented
1179 BadInquiryKeywordHashCrash(inquiry
);
1184 bool InquireUnitState::Inquire(
1185 InquiryKeywordHash inquiry
, std::int64_t &result
) {
1187 case HashInquiryKeyword("NEXTREC"):
1188 if (unit().access
== Access::Direct
) {
1189 result
= unit().currentRecordNumber
;
1192 case HashInquiryKeyword("NUMBER"):
1193 result
= unit().unitNumber();
1195 case HashInquiryKeyword("POS"):
1196 result
= unit().InquirePos();
1198 case HashInquiryKeyword("RECL"):
1199 if (!unit().IsConnected()) {
1201 } else if (unit().access
== Access::Stream
) {
1203 } else if (unit().openRecl
) {
1204 result
= *unit().openRecl
;
1206 result
= std::numeric_limits
<std::int32_t>::max();
1209 case HashInquiryKeyword("SIZE"):
1211 if (unit().IsConnected()) {
1212 if (auto size
{unit().knownSize()}) {
1218 BadInquiryKeywordHashCrash(inquiry
);
1223 InquireNoUnitState::InquireNoUnitState(
1224 const char *sourceFile
, int sourceLine
, int badUnitNumber
)
1225 : NoUnitIoStatementState
{*this, sourceFile
, sourceLine
, badUnitNumber
} {}
1227 bool InquireNoUnitState::Inquire(
1228 InquiryKeywordHash inquiry
, char *result
, std::size_t length
) {
1230 case HashInquiryKeyword("ACCESS"):
1231 case HashInquiryKeyword("ACTION"):
1232 case HashInquiryKeyword("ASYNCHRONOUS"):
1233 case HashInquiryKeyword("BLANK"):
1234 case HashInquiryKeyword("CARRIAGECONTROL"):
1235 case HashInquiryKeyword("CONVERT"):
1236 case HashInquiryKeyword("DECIMAL"):
1237 case HashInquiryKeyword("DELIM"):
1238 case HashInquiryKeyword("FORM"):
1239 case HashInquiryKeyword("NAME"):
1240 case HashInquiryKeyword("PAD"):
1241 case HashInquiryKeyword("POSITION"):
1242 case HashInquiryKeyword("ROUND"):
1243 case HashInquiryKeyword("SIGN"):
1244 ToFortranDefaultCharacter(result
, length
, "UNDEFINED");
1246 case HashInquiryKeyword("DIRECT"):
1247 case HashInquiryKeyword("ENCODING"):
1248 case HashInquiryKeyword("FORMATTED"):
1249 case HashInquiryKeyword("READ"):
1250 case HashInquiryKeyword("READWRITE"):
1251 case HashInquiryKeyword("SEQUENTIAL"):
1252 case HashInquiryKeyword("STREAM"):
1253 case HashInquiryKeyword("WRITE"):
1254 case HashInquiryKeyword("UNFORMATTED"):
1255 ToFortranDefaultCharacter(result
, length
, "UNKNOWN");
1258 BadInquiryKeywordHashCrash(inquiry
);
1263 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry
, bool &result
) {
1265 case HashInquiryKeyword("EXIST"):
1268 case HashInquiryKeyword("NAMED"):
1269 case HashInquiryKeyword("OPENED"):
1270 case HashInquiryKeyword("PENDING"):
1274 BadInquiryKeywordHashCrash(inquiry
);
1279 bool InquireNoUnitState::Inquire(
1280 InquiryKeywordHash inquiry
, std::int64_t, bool &result
) {
1282 case HashInquiryKeyword("PENDING"):
1286 BadInquiryKeywordHashCrash(inquiry
);
1291 bool InquireNoUnitState::Inquire(
1292 InquiryKeywordHash inquiry
, std::int64_t &result
) {
1294 case HashInquiryKeyword("NUMBER"):
1295 result
= badUnitNumber();
1297 case HashInquiryKeyword("NEXTREC"):
1298 case HashInquiryKeyword("POS"):
1299 case HashInquiryKeyword("RECL"):
1300 case HashInquiryKeyword("SIZE"):
1304 BadInquiryKeywordHashCrash(inquiry
);
1309 InquireUnconnectedFileState::InquireUnconnectedFileState(
1310 OwningPtr
<char> &&path
, const char *sourceFile
, int sourceLine
)
1311 : NoUnitIoStatementState
{*this, sourceFile
, sourceLine
}, path_
{std::move(
1314 bool InquireUnconnectedFileState::Inquire(
1315 InquiryKeywordHash inquiry
, char *result
, std::size_t length
) {
1316 const char *str
{nullptr};
1318 case HashInquiryKeyword("ACCESS"):
1319 case HashInquiryKeyword("ACTION"):
1320 case HashInquiryKeyword("ASYNCHRONOUS"):
1321 case HashInquiryKeyword("BLANK"):
1322 case HashInquiryKeyword("CARRIAGECONTROL"):
1323 case HashInquiryKeyword("CONVERT"):
1324 case HashInquiryKeyword("DECIMAL"):
1325 case HashInquiryKeyword("DELIM"):
1326 case HashInquiryKeyword("FORM"):
1327 case HashInquiryKeyword("PAD"):
1328 case HashInquiryKeyword("POSITION"):
1329 case HashInquiryKeyword("ROUND"):
1330 case HashInquiryKeyword("SIGN"):
1333 case HashInquiryKeyword("DIRECT"):
1334 case HashInquiryKeyword("ENCODING"):
1335 case HashInquiryKeyword("FORMATTED"):
1336 case HashInquiryKeyword("SEQUENTIAL"):
1337 case HashInquiryKeyword("STREAM"):
1338 case HashInquiryKeyword("UNFORMATTED"):
1341 case HashInquiryKeyword("READ"):
1342 str
= MayRead(path_
.get()) ? "YES" : "NO";
1344 case HashInquiryKeyword("READWRITE"):
1345 str
= MayReadAndWrite(path_
.get()) ? "YES" : "NO";
1347 case HashInquiryKeyword("WRITE"):
1348 str
= MayWrite(path_
.get()) ? "YES" : "NO";
1350 case HashInquiryKeyword("NAME"):
1353 return true; // result is undefined
1358 ToFortranDefaultCharacter(result
, length
, str
);
1361 BadInquiryKeywordHashCrash(inquiry
);
1366 bool InquireUnconnectedFileState::Inquire(
1367 InquiryKeywordHash inquiry
, bool &result
) {
1369 case HashInquiryKeyword("EXIST"):
1370 result
= IsExtant(path_
.get());
1372 case HashInquiryKeyword("NAMED"):
1375 case HashInquiryKeyword("OPENED"):
1378 case HashInquiryKeyword("PENDING"):
1382 BadInquiryKeywordHashCrash(inquiry
);
1387 bool InquireUnconnectedFileState::Inquire(
1388 InquiryKeywordHash inquiry
, std::int64_t, bool &result
) {
1390 case HashInquiryKeyword("PENDING"):
1394 BadInquiryKeywordHashCrash(inquiry
);
1399 bool InquireUnconnectedFileState::Inquire(
1400 InquiryKeywordHash inquiry
, std::int64_t &result
) {
1402 case HashInquiryKeyword("NEXTREC"):
1403 case HashInquiryKeyword("NUMBER"):
1404 case HashInquiryKeyword("POS"):
1405 case HashInquiryKeyword("RECL"):
1408 case HashInquiryKeyword("SIZE"):
1409 result
= SizeInBytes(path_
.get());
1412 BadInquiryKeywordHashCrash(inquiry
);
1417 InquireIOLengthState::InquireIOLengthState(
1418 const char *sourceFile
, int sourceLine
)
1419 : NoUnitIoStatementState
{*this, sourceFile
, sourceLine
} {}
1421 bool InquireIOLengthState::Emit(const char *, std::size_t bytes
, std::size_t) {
1426 int ErroneousIoStatementState::EndIoStatement() {
1427 SignalPendingError();
1429 unit_
->EndIoStatement();
1431 return IoStatementBase::EndIoStatement();
1434 } // namespace Fortran::runtime::io