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