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

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