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