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