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

source code of flang/runtime/io-stmt.cpp