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