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