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