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