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