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   format_.Finish(*this);
392   return ExternalIoStatementState<DIR>::EndIoStatement();
393 }
394 
395 std::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
396   return std::visit(
397       [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
398 }
399 
400 bool IoStatementState::Emit(
401     const char *data, std::size_t n, std::size_t elementBytes) {
402   return std::visit(
403       [=](auto &x) { return x.get().Emit(data, n, elementBytes); }, u_);
404 }
405 
406 bool IoStatementState::Emit(const char *data, std::size_t n) {
407   return std::visit([=](auto &x) { return x.get().Emit(data, n); }, u_);
408 }
409 
410 bool IoStatementState::Emit(const char16_t *data, std::size_t chars) {
411   return std::visit([=](auto &x) { return x.get().Emit(data, chars); }, u_);
412 }
413 
414 bool IoStatementState::Emit(const char32_t *data, std::size_t chars) {
415   return std::visit([=](auto &x) { return x.get().Emit(data, chars); }, u_);
416 }
417 
418 bool IoStatementState::Receive(
419     char *data, std::size_t n, std::size_t elementBytes) {
420   return std::visit(
421       [=](auto &x) { return x.get().Receive(data, n, elementBytes); }, u_);
422 }
423 
424 std::optional<char32_t> IoStatementState::GetCurrentChar() {
425   return std::visit([&](auto &x) { return x.get().GetCurrentChar(); }, u_);
426 }
427 
428 bool IoStatementState::AdvanceRecord(int n) {
429   return std::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_);
430 }
431 
432 void IoStatementState::BackspaceRecord() {
433   std::visit([](auto &x) { x.get().BackspaceRecord(); }, u_);
434 }
435 
436 void IoStatementState::HandleRelativePosition(std::int64_t n) {
437   std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
438 }
439 
440 void IoStatementState::HandleAbsolutePosition(std::int64_t n) {
441   std::visit([=](auto &x) { x.get().HandleAbsolutePosition(n); }, u_);
442 }
443 
444 int IoStatementState::EndIoStatement() {
445   return std::visit([](auto &x) { return x.get().EndIoStatement(); }, u_);
446 }
447 
448 ConnectionState &IoStatementState::GetConnectionState() {
449   return std::visit(
450       [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); },
451       u_);
452 }
453 
454 MutableModes &IoStatementState::mutableModes() {
455   return std::visit(
456       [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_);
457 }
458 
459 bool IoStatementState::BeginReadingRecord() {
460   return std::visit([](auto &x) { return x.get().BeginReadingRecord(); }, u_);
461 }
462 
463 IoErrorHandler &IoStatementState::GetIoErrorHandler() const {
464   return std::visit(
465       [](auto &x) -> IoErrorHandler & {
466         return static_cast<IoErrorHandler &>(x.get());
467       },
468       u_);
469 }
470 
471 ExternalFileUnit *IoStatementState::GetExternalFileUnit() const {
472   return std::visit([](auto &x) { return x.get().GetExternalFileUnit(); }, u_);
473 }
474 
475 bool IoStatementState::EmitRepeated(char ch, std::size_t n) {
476   return std::visit(
477       [=](auto &x) {
478         for (std::size_t j{0}; j < n; ++j) {
479           if (!x.get().Emit(&ch, 1)) {
480             return false;
481           }
482         }
483         return true;
484       },
485       u_);
486 }
487 
488 bool IoStatementState::EmitField(
489     const char *p, std::size_t length, std::size_t width) {
490   if (width <= 0) {
491     width = static_cast<int>(length);
492   }
493   if (length > static_cast<std::size_t>(width)) {
494     return EmitRepeated('*', width);
495   } else {
496     return EmitRepeated(' ', static_cast<int>(width - length)) &&
497         Emit(p, length);
498   }
499 }
500 
501 std::optional<char32_t> IoStatementState::PrepareInput(
502     const DataEdit &edit, std::optional<int> &remaining) {
503   remaining.reset();
504   if (edit.descriptor == DataEdit::ListDirected) {
505     GetNextNonBlank();
506   } else {
507     if (edit.width.value_or(0) > 0) {
508       remaining = *edit.width;
509     }
510     SkipSpaces(remaining);
511   }
512   return NextInField(remaining);
513 }
514 
515 std::optional<char32_t> IoStatementState::SkipSpaces(
516     std::optional<int> &remaining) {
517   while (!remaining || *remaining > 0) {
518     if (auto ch{GetCurrentChar()}) {
519       if (*ch != ' ' && *ch != '\t') {
520         return ch;
521       }
522       HandleRelativePosition(1);
523       if (remaining) {
524         --*remaining;
525       }
526     } else {
527       break;
528     }
529   }
530   return std::nullopt;
531 }
532 
533 std::optional<char32_t> IoStatementState::NextInField(
534     std::optional<int> &remaining) {
535   if (!remaining) { // list-directed or NAMELIST: check for separators
536     if (auto next{GetCurrentChar()}) {
537       switch (*next) {
538       case ' ':
539       case '\t':
540       case ',':
541       case ';':
542       case '/':
543       case '(':
544       case ')':
545       case '\'':
546       case '"':
547       case '*':
548       case '\n': // for stream access
549         break;
550       default:
551         HandleRelativePosition(1);
552         return next;
553       }
554     }
555   } else if (*remaining > 0) {
556     if (auto next{GetCurrentChar()}) {
557       --*remaining;
558       HandleRelativePosition(1);
559       return next;
560     }
561     const ConnectionState &connection{GetConnectionState()};
562     if (!connection.IsAtEOF() && connection.recordLength &&
563         connection.positionInRecord >= *connection.recordLength) {
564       IoErrorHandler &handler{GetIoErrorHandler()};
565       if (mutableModes().nonAdvancing) {
566         handler.SignalEor();
567       } else if (connection.isFixedRecordLength && !connection.modes.pad) {
568         handler.SignalError(IostatRecordReadOverrun);
569       }
570       if (connection.modes.pad) { // PAD='YES'
571         --*remaining;
572         return std::optional<char32_t>{' '};
573       }
574     }
575   }
576   return std::nullopt;
577 }
578 
579 std::optional<char32_t> IoStatementState::GetNextNonBlank() {
580   auto ch{GetCurrentChar()};
581   bool inNamelist{GetConnectionState().modes.inNamelist};
582   while (!ch || *ch == ' ' || *ch == '\t' || (inNamelist && *ch == '!')) {
583     if (ch && (*ch == ' ' || *ch == '\t')) {
584       HandleRelativePosition(1);
585     } else if (!AdvanceRecord()) {
586       return std::nullopt;
587     }
588     ch = GetCurrentChar();
589   }
590   return ch;
591 }
592 
593 bool IoStatementState::Inquire(
594     InquiryKeywordHash inquiry, char *out, std::size_t chars) {
595   return std::visit(
596       [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);
597 }
598 
599 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {
600   return std::visit([&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);
601 }
602 
603 bool IoStatementState::Inquire(
604     InquiryKeywordHash inquiry, std::int64_t id, bool &out) {
605   return std::visit(
606       [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);
607 }
608 
609 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
610   return std::visit([&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
611 }
612 
613 bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
614     IoStatementState &io, std::size_t length, bool isCharacter) {
615   if (length == 0) {
616     return true;
617   }
618   const ConnectionState &connection{io.GetConnectionState()};
619   int space{connection.positionInRecord == 0 ||
620       !(isCharacter && lastWasUndelimitedCharacter())};
621   set_lastWasUndelimitedCharacter(false);
622   if (connection.NeedAdvance(space + length)) {
623     return io.AdvanceRecord();
624   }
625   if (space) {
626     return io.Emit(" ", 1);
627   }
628   return true;
629 }
630 
631 std::optional<DataEdit>
632 ListDirectedStatementState<Direction::Output>::GetNextDataEdit(
633     IoStatementState &io, int maxRepeat) {
634   DataEdit edit;
635   edit.descriptor = DataEdit::ListDirected;
636   edit.repeat = maxRepeat;
637   edit.modes = io.mutableModes();
638   return edit;
639 }
640 
641 std::optional<DataEdit>
642 ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
643     IoStatementState &io, int maxRepeat) {
644   // N.B. list-directed transfers cannot be nonadvancing (C1221)
645   ConnectionState &connection{io.GetConnectionState()};
646   DataEdit edit;
647   edit.descriptor = DataEdit::ListDirected;
648   edit.repeat = 1; // may be overridden below
649   edit.modes = connection.modes;
650   if (hitSlash_) { // everything after '/' is nullified
651     edit.descriptor = DataEdit::ListDirectedNullValue;
652     return edit;
653   }
654   char32_t comma{','};
655   if (io.mutableModes().editingFlags & decimalComma) {
656     comma = ';';
657   }
658   if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress
659     RUNTIME_CHECK(
660         io.GetIoErrorHandler(), connection.resumptionRecordNumber.has_value());
661     while (connection.currentRecordNumber >
662         connection.resumptionRecordNumber.value_or(
663             connection.currentRecordNumber)) {
664       io.BackspaceRecord();
665     }
666     connection.HandleAbsolutePosition(repeatPositionInRecord_);
667     if (!imaginaryPart_) {
668       edit.repeat = std::min<int>(remaining_, maxRepeat);
669       auto ch{io.GetCurrentChar()};
670       if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) {
671         // "r*" repeated null
672         edit.descriptor = DataEdit::ListDirectedNullValue;
673       }
674     }
675     remaining_ -= edit.repeat;
676     if (remaining_ <= 0) {
677       connection.resumptionRecordNumber.reset();
678     }
679     return edit;
680   }
681   // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018
682   if (imaginaryPart_) {
683     imaginaryPart_ = false;
684   } else if (realPart_) {
685     realPart_ = false;
686     imaginaryPart_ = true;
687     edit.descriptor = DataEdit::ListDirectedImaginaryPart;
688   }
689   auto ch{io.GetNextNonBlank()};
690   if (ch && *ch == comma && eatComma_) {
691     // Consume comma & whitespace after previous item.
692     // This includes the comma between real and imaginary components
693     // in list-directed/NAMELIST complex input.
694     io.HandleRelativePosition(1);
695     ch = io.GetNextNonBlank();
696   }
697   eatComma_ = true;
698   if (!ch) {
699     return std::nullopt;
700   }
701   if (*ch == '/') {
702     hitSlash_ = true;
703     edit.descriptor = DataEdit::ListDirectedNullValue;
704     return edit;
705   }
706   if (*ch == comma) { // separator: null value
707     edit.descriptor = DataEdit::ListDirectedNullValue;
708     return edit;
709   }
710   if (imaginaryPart_) { // can't repeat components
711     return edit;
712   }
713   if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count
714     auto start{connection.positionInRecord};
715     int r{0};
716     do {
717       static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};
718       if (r >= clamp) {
719         r = 0;
720         break;
721       }
722       r = 10 * r + (*ch - '0');
723       io.HandleRelativePosition(1);
724       ch = io.GetCurrentChar();
725     } while (ch && *ch >= '0' && *ch <= '9');
726     if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero
727       io.HandleRelativePosition(1);
728       ch = io.GetCurrentChar();
729       if (ch && *ch == '/') { // r*/
730         hitSlash_ = true;
731         edit.descriptor = DataEdit::ListDirectedNullValue;
732         return edit;
733       }
734       if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { // "r*" null
735         edit.descriptor = DataEdit::ListDirectedNullValue;
736       }
737       edit.repeat = std::min<int>(r, maxRepeat);
738       remaining_ = r - edit.repeat;
739       if (remaining_ > 0) {
740         connection.resumptionRecordNumber = connection.currentRecordNumber;
741       } else {
742         connection.resumptionRecordNumber.reset();
743       }
744       repeatPositionInRecord_ = connection.positionInRecord;
745     } else { // not a repetition count, just an integer value; rewind
746       connection.positionInRecord = start;
747     }
748   }
749   if (!imaginaryPart_ && ch && *ch == '(') {
750     realPart_ = true;
751     io.HandleRelativePosition(1);
752     edit.descriptor = DataEdit::ListDirectedRealPart;
753   }
754   return edit;
755 }
756 
757 template <Direction DIR>
758 bool ExternalUnformattedIoStatementState<DIR>::Receive(
759     char *data, std::size_t bytes, std::size_t elementBytes) {
760   if constexpr (DIR == Direction::Output) {
761     this->Crash("ExternalUnformattedIoStatementState::Receive() called for "
762                 "output statement");
763   }
764   return this->unit().Receive(data, bytes, elementBytes, *this);
765 }
766 
767 template <Direction DIR>
768 ChildIoStatementState<DIR>::ChildIoStatementState(
769     ChildIo &child, const char *sourceFile, int sourceLine)
770     : IoStatementBase{sourceFile, sourceLine}, child_{child} {}
771 
772 template <Direction DIR>
773 MutableModes &ChildIoStatementState<DIR>::mutableModes() {
774   return child_.parent().mutableModes();
775 }
776 
777 template <Direction DIR>
778 ConnectionState &ChildIoStatementState<DIR>::GetConnectionState() {
779   return child_.parent().GetConnectionState();
780 }
781 
782 template <Direction DIR>
783 ExternalFileUnit *ChildIoStatementState<DIR>::GetExternalFileUnit() const {
784   return child_.parent().GetExternalFileUnit();
785 }
786 
787 template <Direction DIR> int ChildIoStatementState<DIR>::EndIoStatement() {
788   auto result{IoStatementBase::EndIoStatement()};
789   child_.EndIoStatement(); // annihilates *this in child_.u_
790   return result;
791 }
792 
793 template <Direction DIR>
794 bool ChildIoStatementState<DIR>::Emit(
795     const char *data, std::size_t bytes, std::size_t elementBytes) {
796   return child_.parent().Emit(data, bytes, elementBytes);
797 }
798 
799 template <Direction DIR>
800 bool ChildIoStatementState<DIR>::Emit(const char *data, std::size_t bytes) {
801   return child_.parent().Emit(data, bytes);
802 }
803 
804 template <Direction DIR>
805 bool ChildIoStatementState<DIR>::Emit(const char16_t *data, std::size_t chars) {
806   return child_.parent().Emit(data, chars);
807 }
808 
809 template <Direction DIR>
810 bool ChildIoStatementState<DIR>::Emit(const char32_t *data, std::size_t chars) {
811   return child_.parent().Emit(data, chars);
812 }
813 
814 template <Direction DIR>
815 std::optional<char32_t> ChildIoStatementState<DIR>::GetCurrentChar() {
816   return child_.parent().GetCurrentChar();
817 }
818 
819 template <Direction DIR>
820 void ChildIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
821   return child_.parent().HandleAbsolutePosition(n);
822 }
823 
824 template <Direction DIR>
825 void ChildIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
826   return child_.parent().HandleRelativePosition(n);
827 }
828 
829 template <Direction DIR, typename CHAR>
830 ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(
831     ChildIo &child, const CHAR *format, std::size_t formatLength,
832     const char *sourceFile, int sourceLine)
833     : ChildIoStatementState<DIR>{child, sourceFile, sourceLine},
834       mutableModes_{child.parent().mutableModes()}, format_{*this, format,
835                                                         formatLength} {}
836 
837 template <Direction DIR, typename CHAR>
838 int ChildFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
839   format_.Finish(*this);
840   return ChildIoStatementState<DIR>::EndIoStatement();
841 }
842 
843 template <Direction DIR, typename CHAR>
844 bool ChildFormattedIoStatementState<DIR, CHAR>::AdvanceRecord(int) {
845   return false; // no can do in a child I/O
846 }
847 
848 template <Direction DIR>
849 bool ChildUnformattedIoStatementState<DIR>::Receive(
850     char *data, std::size_t bytes, std::size_t elementBytes) {
851   return this->child().parent().Receive(data, bytes, elementBytes);
852 }
853 
854 template class InternalIoStatementState<Direction::Output>;
855 template class InternalIoStatementState<Direction::Input>;
856 template class InternalFormattedIoStatementState<Direction::Output>;
857 template class InternalFormattedIoStatementState<Direction::Input>;
858 template class InternalListIoStatementState<Direction::Output>;
859 template class InternalListIoStatementState<Direction::Input>;
860 template class ExternalIoStatementState<Direction::Output>;
861 template class ExternalIoStatementState<Direction::Input>;
862 template class ExternalFormattedIoStatementState<Direction::Output>;
863 template class ExternalFormattedIoStatementState<Direction::Input>;
864 template class ExternalListIoStatementState<Direction::Output>;
865 template class ExternalListIoStatementState<Direction::Input>;
866 template class ExternalUnformattedIoStatementState<Direction::Output>;
867 template class ExternalUnformattedIoStatementState<Direction::Input>;
868 template class ChildIoStatementState<Direction::Output>;
869 template class ChildIoStatementState<Direction::Input>;
870 template class ChildFormattedIoStatementState<Direction::Output>;
871 template class ChildFormattedIoStatementState<Direction::Input>;
872 template class ChildListIoStatementState<Direction::Output>;
873 template class ChildListIoStatementState<Direction::Input>;
874 template class ChildUnformattedIoStatementState<Direction::Output>;
875 template class ChildUnformattedIoStatementState<Direction::Input>;
876 
877 int ExternalMiscIoStatementState::EndIoStatement() {
878   ExternalFileUnit &ext{unit()};
879   switch (which_) {
880   case Flush:
881     ext.FlushOutput(*this);
882     std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
883     break;
884   case Backspace:
885     ext.BackspaceRecord(*this);
886     break;
887   case Endfile:
888     ext.Endfile(*this);
889     break;
890   case Rewind:
891     ext.Rewind(*this);
892     break;
893   }
894   return ExternalIoStatementBase::EndIoStatement();
895 }
896 
897 InquireUnitState::InquireUnitState(
898     ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
899     : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
900 
901 bool InquireUnitState::Inquire(
902     InquiryKeywordHash inquiry, char *result, std::size_t length) {
903   if (unit().createdForInternalChildIo()) {
904     SignalError(IostatInquireInternalUnit,
905         "INQUIRE of unit created for defined derived type I/O of an internal "
906         "unit");
907     return false;
908   }
909   const char *str{nullptr};
910   switch (inquiry) {
911   case HashInquiryKeyword("ACCESS"):
912     switch (unit().access) {
913     case Access::Sequential:
914       str = "SEQUENTIAL";
915       break;
916     case Access::Direct:
917       str = "DIRECT";
918       break;
919     case Access::Stream:
920       str = "STREAM";
921       break;
922     }
923     break;
924   case HashInquiryKeyword("ACTION"):
925     str = unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" : "READ";
926     break;
927   case HashInquiryKeyword("ASYNCHRONOUS"):
928     str = unit().mayAsynchronous() ? "YES" : "NO";
929     break;
930   case HashInquiryKeyword("BLANK"):
931     str = unit().isUnformatted.value_or(true)   ? "UNDEFINED"
932         : unit().modes.editingFlags & blankZero ? "ZERO"
933                                                 : "NULL";
934     break;
935   case HashInquiryKeyword("CARRIAGECONTROL"):
936     str = "LIST";
937     break;
938   case HashInquiryKeyword("CONVERT"):
939     str = unit().swapEndianness() ? "SWAP" : "NATIVE";
940     break;
941   case HashInquiryKeyword("DECIMAL"):
942     str = unit().isUnformatted.value_or(true)      ? "UNDEFINED"
943         : unit().modes.editingFlags & decimalComma ? "COMMA"
944                                                    : "POINT";
945     break;
946   case HashInquiryKeyword("DELIM"):
947     if (unit().isUnformatted.value_or(true)) {
948       str = "UNDEFINED";
949     } else {
950       switch (unit().modes.delim) {
951       case '\'':
952         str = "APOSTROPHE";
953         break;
954       case '"':
955         str = "QUOTE";
956         break;
957       default:
958         str = "NONE";
959         break;
960       }
961     }
962     break;
963   case HashInquiryKeyword("DIRECT"):
964     str = unit().access == Access::Direct ||
965             (unit().mayPosition() && unit().isFixedRecordLength)
966         ? "YES"
967         : "NO";
968     break;
969   case HashInquiryKeyword("ENCODING"):
970     str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
971         : unit().isUTF8                       ? "UTF-8"
972                                               : "ASCII";
973     break;
974   case HashInquiryKeyword("FORM"):
975     str = !unit().isUnformatted ? "UNDEFINED"
976         : *unit().isUnformatted ? "UNFORMATTED"
977                                 : "FORMATTED";
978     break;
979   case HashInquiryKeyword("FORMATTED"):
980     str = !unit().isUnformatted ? "UNKNOWN"
981         : *unit().isUnformatted ? "NO"
982                                 : "YES";
983     break;
984   case HashInquiryKeyword("NAME"):
985     str = unit().path();
986     if (!str) {
987       return true; // result is undefined
988     }
989     break;
990   case HashInquiryKeyword("PAD"):
991     str = unit().isUnformatted.value_or(true) ? "UNDEFINED"
992         : unit().modes.pad                    ? "YES"
993                                               : "NO";
994     break;
995   case HashInquiryKeyword("POSITION"):
996     if (unit().access == Access::Direct) {
997       str = "UNDEFINED";
998     } else {
999       auto size{unit().knownSize()};
1000       auto pos{unit().position()};
1001       if (pos == size.value_or(pos + 1)) {
1002         str = "APPEND";
1003       } else if (pos == 0) {
1004         str = "REWIND";
1005       } else {
1006         str = "ASIS"; // processor-dependent & no common behavior
1007       }
1008     }
1009     break;
1010   case HashInquiryKeyword("READ"):
1011     str = unit().mayRead() ? "YES" : "NO";
1012     break;
1013   case HashInquiryKeyword("READWRITE"):
1014     str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO";
1015     break;
1016   case HashInquiryKeyword("ROUND"):
1017     if (unit().isUnformatted.value_or(true)) {
1018       str = "UNDEFINED";
1019     } else {
1020       switch (unit().modes.round) {
1021       case decimal::FortranRounding::RoundNearest:
1022         str = "NEAREST";
1023         break;
1024       case decimal::FortranRounding::RoundUp:
1025         str = "UP";
1026         break;
1027       case decimal::FortranRounding::RoundDown:
1028         str = "DOWN";
1029         break;
1030       case decimal::FortranRounding::RoundToZero:
1031         str = "ZERO";
1032         break;
1033       case decimal::FortranRounding::RoundCompatible:
1034         str = "COMPATIBLE";
1035         break;
1036       }
1037     }
1038     break;
1039   case HashInquiryKeyword("SEQUENTIAL"):
1040     // "NO" for Direct, since Sequential would not work if
1041     // the unit were reopened without RECL=.
1042     str = unit().access == Access::Sequential ? "YES" : "NO";
1043     break;
1044   case HashInquiryKeyword("SIGN"):
1045     str = unit().isUnformatted.value_or(true)  ? "UNDEFINED"
1046         : unit().modes.editingFlags & signPlus ? "PLUS"
1047                                                : "SUPPRESS";
1048     break;
1049   case HashInquiryKeyword("STREAM"):
1050     str = unit().access == Access::Stream ? "YES" : "NO";
1051     break;
1052   case HashInquiryKeyword("WRITE"):
1053     str = unit().mayWrite() ? "YES" : "NO";
1054     break;
1055   case HashInquiryKeyword("UNFORMATTED"):
1056     str = !unit().isUnformatted ? "UNKNOWN"
1057         : *unit().isUnformatted ? "YES"
1058                                 : "NO";
1059     break;
1060   }
1061   if (str) {
1062     ToFortranDefaultCharacter(result, length, str);
1063     return true;
1064   } else {
1065     BadInquiryKeywordHashCrash(inquiry);
1066     return false;
1067   }
1068 }
1069 
1070 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1071   switch (inquiry) {
1072   case HashInquiryKeyword("EXIST"):
1073     result = true;
1074     return true;
1075   case HashInquiryKeyword("NAMED"):
1076     result = unit().path() != nullptr;
1077     return true;
1078   case HashInquiryKeyword("OPENED"):
1079     result = true;
1080     return true;
1081   case HashInquiryKeyword("PENDING"):
1082     result = false; // asynchronous I/O is not implemented
1083     return true;
1084   default:
1085     BadInquiryKeywordHashCrash(inquiry);
1086     return false;
1087   }
1088 }
1089 
1090 bool InquireUnitState::Inquire(
1091     InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1092   switch (inquiry) {
1093   case HashInquiryKeyword("PENDING"):
1094     result = false; // asynchronous I/O is not implemented
1095     return true;
1096   default:
1097     BadInquiryKeywordHashCrash(inquiry);
1098     return false;
1099   }
1100 }
1101 
1102 bool InquireUnitState::Inquire(
1103     InquiryKeywordHash inquiry, std::int64_t &result) {
1104   switch (inquiry) {
1105   case HashInquiryKeyword("NEXTREC"):
1106     if (unit().access == Access::Direct) {
1107       result = unit().currentRecordNumber;
1108     }
1109     return true;
1110   case HashInquiryKeyword("NUMBER"):
1111     result = unit().unitNumber();
1112     return true;
1113   case HashInquiryKeyword("POS"):
1114     result = unit().position();
1115     return true;
1116   case HashInquiryKeyword("RECL"):
1117     if (unit().access == Access::Stream) {
1118       result = -2;
1119     } else if (unit().isFixedRecordLength && unit().recordLength) {
1120       result = *unit().recordLength;
1121     } else {
1122       result = std::numeric_limits<std::uint32_t>::max();
1123     }
1124     return true;
1125   case HashInquiryKeyword("SIZE"):
1126     if (auto size{unit().knownSize()}) {
1127       result = *size;
1128     } else {
1129       result = -1;
1130     }
1131     return true;
1132   default:
1133     BadInquiryKeywordHashCrash(inquiry);
1134     return false;
1135   }
1136 }
1137 
1138 InquireNoUnitState::InquireNoUnitState(const char *sourceFile, int sourceLine)
1139     : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
1140 
1141 bool InquireNoUnitState::Inquire(
1142     InquiryKeywordHash inquiry, char *result, std::size_t length) {
1143   switch (inquiry) {
1144   case HashInquiryKeyword("ACCESS"):
1145   case HashInquiryKeyword("ACTION"):
1146   case HashInquiryKeyword("ASYNCHRONOUS"):
1147   case HashInquiryKeyword("BLANK"):
1148   case HashInquiryKeyword("CARRIAGECONTROL"):
1149   case HashInquiryKeyword("CONVERT"):
1150   case HashInquiryKeyword("DECIMAL"):
1151   case HashInquiryKeyword("DELIM"):
1152   case HashInquiryKeyword("FORM"):
1153   case HashInquiryKeyword("NAME"):
1154   case HashInquiryKeyword("PAD"):
1155   case HashInquiryKeyword("POSITION"):
1156   case HashInquiryKeyword("ROUND"):
1157   case HashInquiryKeyword("SIGN"):
1158     ToFortranDefaultCharacter(result, length, "UNDEFINED");
1159     return true;
1160   case HashInquiryKeyword("DIRECT"):
1161   case HashInquiryKeyword("ENCODING"):
1162   case HashInquiryKeyword("FORMATTED"):
1163   case HashInquiryKeyword("READ"):
1164   case HashInquiryKeyword("READWRITE"):
1165   case HashInquiryKeyword("SEQUENTIAL"):
1166   case HashInquiryKeyword("STREAM"):
1167   case HashInquiryKeyword("WRITE"):
1168   case HashInquiryKeyword("UNFORMATTED"):
1169     ToFortranDefaultCharacter(result, length, "UNKNONN");
1170     return true;
1171   default:
1172     BadInquiryKeywordHashCrash(inquiry);
1173     return false;
1174   }
1175 }
1176 
1177 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1178   switch (inquiry) {
1179   case HashInquiryKeyword("EXIST"):
1180     result = true;
1181     return true;
1182   case HashInquiryKeyword("NAMED"):
1183   case HashInquiryKeyword("OPENED"):
1184   case HashInquiryKeyword("PENDING"):
1185     result = false;
1186     return true;
1187   default:
1188     BadInquiryKeywordHashCrash(inquiry);
1189     return false;
1190   }
1191 }
1192 
1193 bool InquireNoUnitState::Inquire(
1194     InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1195   switch (inquiry) {
1196   case HashInquiryKeyword("PENDING"):
1197     result = false;
1198     return true;
1199   default:
1200     BadInquiryKeywordHashCrash(inquiry);
1201     return false;
1202   }
1203 }
1204 
1205 bool InquireNoUnitState::Inquire(
1206     InquiryKeywordHash inquiry, std::int64_t &result) {
1207   switch (inquiry) {
1208   case HashInquiryKeyword("NEXTREC"):
1209   case HashInquiryKeyword("NUMBER"):
1210   case HashInquiryKeyword("POS"):
1211   case HashInquiryKeyword("RECL"):
1212   case HashInquiryKeyword("SIZE"):
1213     result = -1;
1214     return true;
1215   default:
1216     BadInquiryKeywordHashCrash(inquiry);
1217     return false;
1218   }
1219 }
1220 
1221 InquireUnconnectedFileState::InquireUnconnectedFileState(
1222     OwningPtr<char> &&path, const char *sourceFile, int sourceLine)
1223     : NoUnitIoStatementState{sourceFile, sourceLine, *this}, path_{std::move(
1224                                                                  path)} {}
1225 
1226 bool InquireUnconnectedFileState::Inquire(
1227     InquiryKeywordHash inquiry, char *result, std::size_t length) {
1228   const char *str{nullptr};
1229   switch (inquiry) {
1230   case HashInquiryKeyword("ACCESS"):
1231   case HashInquiryKeyword("ACTION"):
1232   case HashInquiryKeyword("ASYNCHRONOUS"):
1233   case HashInquiryKeyword("BLANK"):
1234   case HashInquiryKeyword("CARRIAGECONTROL"):
1235   case HashInquiryKeyword("CONVERT"):
1236   case HashInquiryKeyword("DECIMAL"):
1237   case HashInquiryKeyword("DELIM"):
1238   case HashInquiryKeyword("FORM"):
1239   case HashInquiryKeyword("PAD"):
1240   case HashInquiryKeyword("POSITION"):
1241   case HashInquiryKeyword("ROUND"):
1242   case HashInquiryKeyword("SIGN"):
1243     str = "UNDEFINED";
1244     break;
1245   case HashInquiryKeyword("DIRECT"):
1246   case HashInquiryKeyword("ENCODING"):
1247   case HashInquiryKeyword("FORMATTED"):
1248   case HashInquiryKeyword("SEQUENTIAL"):
1249   case HashInquiryKeyword("STREAM"):
1250   case HashInquiryKeyword("UNFORMATTED"):
1251     str = "UNKNONN";
1252     break;
1253   case HashInquiryKeyword("READ"):
1254     str = MayRead(path_.get()) ? "YES" : "NO";
1255     break;
1256   case HashInquiryKeyword("READWRITE"):
1257     str = MayReadAndWrite(path_.get()) ? "YES" : "NO";
1258     break;
1259   case HashInquiryKeyword("WRITE"):
1260     str = MayWrite(path_.get()) ? "YES" : "NO";
1261     break;
1262   case HashInquiryKeyword("NAME"):
1263     str = path_.get();
1264     return true;
1265   }
1266   if (str) {
1267     ToFortranDefaultCharacter(result, length, str);
1268     return true;
1269   } else {
1270     BadInquiryKeywordHashCrash(inquiry);
1271     return false;
1272   }
1273 }
1274 
1275 bool InquireUnconnectedFileState::Inquire(
1276     InquiryKeywordHash inquiry, bool &result) {
1277   switch (inquiry) {
1278   case HashInquiryKeyword("EXIST"):
1279     result = IsExtant(path_.get());
1280     return true;
1281   case HashInquiryKeyword("NAMED"):
1282     result = true;
1283     return true;
1284   case HashInquiryKeyword("OPENED"):
1285     result = false;
1286     return true;
1287   case HashInquiryKeyword("PENDING"):
1288     result = false;
1289     return true;
1290   default:
1291     BadInquiryKeywordHashCrash(inquiry);
1292     return false;
1293   }
1294 }
1295 
1296 bool InquireUnconnectedFileState::Inquire(
1297     InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1298   switch (inquiry) {
1299   case HashInquiryKeyword("PENDING"):
1300     result = false;
1301     return true;
1302   default:
1303     BadInquiryKeywordHashCrash(inquiry);
1304     return false;
1305   }
1306 }
1307 
1308 bool InquireUnconnectedFileState::Inquire(
1309     InquiryKeywordHash inquiry, std::int64_t &result) {
1310   switch (inquiry) {
1311   case HashInquiryKeyword("NEXTREC"):
1312   case HashInquiryKeyword("NUMBER"):
1313   case HashInquiryKeyword("POS"):
1314   case HashInquiryKeyword("RECL"):
1315   case HashInquiryKeyword("SIZE"):
1316     result = -1;
1317     return true;
1318   default:
1319     BadInquiryKeywordHashCrash(inquiry);
1320     return false;
1321   }
1322 }
1323 
1324 InquireIOLengthState::InquireIOLengthState(
1325     const char *sourceFile, int sourceLine)
1326     : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
1327 
1328 } // namespace Fortran::runtime::io
1329