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