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

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