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 SignalPendingError(); 291 IoStatementBase::CompleteOperation(); 292 } 293 294 int NoUnitIoStatementState::EndIoStatement() { 295 CompleteOperation(); 296 auto result{IoStatementBase::EndIoStatement()}; 297 FreeMemory(this); 298 return result; 299 } 300 301 template <Direction DIR> 302 ExternalIoStatementState<DIR>::ExternalIoStatementState( 303 ExternalFileUnit &unit, const char *sourceFile, int sourceLine) 304 : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{ 305 unit.modes} { 306 if constexpr (DIR == Direction::Output) { 307 // If the last statement was a non-advancing IO input statement, the unit 308 // furthestPositionInRecord was not advanced, but the positionInRecord may 309 // have been advanced. Advance furthestPositionInRecord here to avoid 310 // overwriting the part of the record that has been read with blanks. 311 unit.furthestPositionInRecord = 312 std::max(unit.furthestPositionInRecord, unit.positionInRecord); 313 } 314 } 315 316 template <Direction DIR> 317 void ExternalIoStatementState<DIR>::CompleteOperation() { 318 if (completedOperation()) { 319 return; 320 } 321 if constexpr (DIR == Direction::Input) { 322 BeginReadingRecord(); // in case there were no I/O items 323 if (mutableModes().nonAdvancing && !InError()) { 324 unit().leftTabLimit = unit().furthestPositionInRecord; 325 } else { 326 FinishReadingRecord(); 327 } 328 } else { // output 329 if (mutableModes().nonAdvancing) { 330 // Make effects of positioning past the last Emit() visible with blanks. 331 std::int64_t n{unit().positionInRecord - unit().furthestPositionInRecord}; 332 while (n-- > 0 && unit().Emit(" ", 1, 1, *this)) { 333 } 334 unit().leftTabLimit = unit().positionInRecord; 335 } else { 336 unit().AdvanceRecord(*this); 337 } 338 unit().FlushIfTerminal(*this); 339 } 340 return IoStatementBase::CompleteOperation(); 341 } 342 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 const auto &modes{mutableModes()}; 681 if (modes.nonAdvancing) { 682 if (connection.access == Access::Stream && 683 connection.unterminatedRecord) { 684 // Reading final unterminated record left by a 685 // non-advancing WRITE on a stream file prior to 686 // positioning or ENDFILE. 687 handler.SignalEnd(); 688 } else { 689 handler.SignalEor(); 690 } 691 } else if (!modes.pad) { 692 handler.SignalError(IostatRecordReadOverrun); 693 } 694 return modes.pad; // PAD='YES' 695 } 696 } 697 } 698 return false; 699 } 700 701 bool IoStatementState::Inquire( 702 InquiryKeywordHash inquiry, char *out, std::size_t chars) { 703 return common::visit( 704 [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_); 705 } 706 707 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) { 708 return common::visit( 709 [&](auto &x) { return x.get().Inquire(inquiry, out); }, u_); 710 } 711 712 bool IoStatementState::Inquire( 713 InquiryKeywordHash inquiry, std::int64_t id, bool &out) { 714 return common::visit( 715 [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_); 716 } 717 718 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) { 719 return common::visit( 720 [&](auto &x) { return x.get().Inquire(inquiry, n); }, u_); 721 } 722 723 void IoStatementState::GotChar(int n) { 724 if (auto *formattedIn{ 725 get_if<FormattedIoStatementState<Direction::Input>>()}) { 726 formattedIn->GotChar(n); 727 } else { 728 GetIoErrorHandler().Crash("IoStatementState::GotChar() called for " 729 "statement that is not formatted input"); 730 } 731 } 732 733 std::size_t 734 FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const { 735 return chars_; 736 } 737 738 void FormattedIoStatementState<Direction::Input>::GotChar(int n) { 739 chars_ += n; 740 } 741 742 bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance( 743 IoStatementState &io, std::size_t length, bool isCharacter) { 744 if (length == 0) { 745 return true; 746 } 747 const ConnectionState &connection{io.GetConnectionState()}; 748 int space{connection.positionInRecord == 0 || 749 !(isCharacter && lastWasUndelimitedCharacter())}; 750 set_lastWasUndelimitedCharacter(false); 751 if (connection.NeedAdvance(space + length)) { 752 return io.AdvanceRecord(); 753 } 754 if (space) { 755 return io.Emit(" ", 1); 756 } 757 return true; 758 } 759 760 std::optional<DataEdit> 761 ListDirectedStatementState<Direction::Output>::GetNextDataEdit( 762 IoStatementState &io, int maxRepeat) { 763 DataEdit edit; 764 edit.descriptor = DataEdit::ListDirected; 765 edit.repeat = maxRepeat; 766 edit.modes = io.mutableModes(); 767 return edit; 768 } 769 770 std::optional<DataEdit> 771 ListDirectedStatementState<Direction::Input>::GetNextDataEdit( 772 IoStatementState &io, int maxRepeat) { 773 // N.B. list-directed transfers cannot be nonadvancing (C1221) 774 ConnectionState &connection{io.GetConnectionState()}; 775 DataEdit edit; 776 edit.descriptor = DataEdit::ListDirected; 777 edit.repeat = 1; // may be overridden below 778 edit.modes = io.mutableModes(); 779 if (hitSlash_) { // everything after '/' is nullified 780 edit.descriptor = DataEdit::ListDirectedNullValue; 781 return edit; 782 } 783 char32_t comma{','}; 784 if (edit.modes.editingFlags & decimalComma) { 785 comma = ';'; 786 } 787 std::size_t byteCount{0}; 788 if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress 789 RUNTIME_CHECK(io.GetIoErrorHandler(), repeatPosition_.has_value()); 790 repeatPosition_.reset(); // restores the saved position 791 if (!imaginaryPart_) { 792 edit.repeat = std::min<int>(remaining_, maxRepeat); 793 auto ch{io.GetCurrentChar(byteCount)}; 794 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { 795 // "r*" repeated null 796 edit.descriptor = DataEdit::ListDirectedNullValue; 797 } 798 } 799 remaining_ -= edit.repeat; 800 if (remaining_ > 0) { 801 repeatPosition_.emplace(io); 802 } 803 if (!imaginaryPart_) { 804 return edit; 805 } 806 } 807 // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018 808 if (imaginaryPart_) { 809 imaginaryPart_ = false; 810 } else if (realPart_) { 811 realPart_ = false; 812 imaginaryPart_ = true; 813 edit.descriptor = DataEdit::ListDirectedImaginaryPart; 814 } 815 auto ch{io.GetNextNonBlank(byteCount)}; 816 if (ch && *ch == comma && eatComma_) { 817 // Consume comma & whitespace after previous item. 818 // This includes the comma between real and imaginary components 819 // in list-directed/NAMELIST complex input. 820 // (When DECIMAL='COMMA', the comma is actually a semicolon.) 821 io.HandleRelativePosition(byteCount); 822 ch = io.GetNextNonBlank(byteCount); 823 } 824 eatComma_ = true; 825 if (!ch) { 826 return std::nullopt; 827 } 828 if (*ch == '/') { 829 hitSlash_ = true; 830 edit.descriptor = DataEdit::ListDirectedNullValue; 831 return edit; 832 } 833 if (*ch == comma) { // separator: null value 834 edit.descriptor = DataEdit::ListDirectedNullValue; 835 return edit; 836 } 837 if (imaginaryPart_) { // can't repeat components 838 return edit; 839 } 840 if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count 841 auto start{connection.positionInRecord}; 842 int r{0}; 843 do { 844 static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10}; 845 if (r >= clamp) { 846 r = 0; 847 break; 848 } 849 r = 10 * r + (*ch - '0'); 850 io.HandleRelativePosition(byteCount); 851 ch = io.GetCurrentChar(byteCount); 852 } while (ch && *ch >= '0' && *ch <= '9'); 853 if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero 854 io.HandleRelativePosition(byteCount); 855 ch = io.GetCurrentChar(byteCount); 856 if (ch && *ch == '/') { // r*/ 857 hitSlash_ = true; 858 edit.descriptor = DataEdit::ListDirectedNullValue; 859 return edit; 860 } 861 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { // "r*" null 862 edit.descriptor = DataEdit::ListDirectedNullValue; 863 } 864 edit.repeat = std::min<int>(r, maxRepeat); 865 remaining_ = r - edit.repeat; 866 if (remaining_ > 0) { 867 repeatPosition_.emplace(io); 868 } 869 } else { // not a repetition count, just an integer value; rewind 870 connection.positionInRecord = start; 871 } 872 } 873 if (!imaginaryPart_ && ch && *ch == '(') { 874 realPart_ = true; 875 io.HandleRelativePosition(byteCount); 876 edit.descriptor = DataEdit::ListDirectedRealPart; 877 } 878 return edit; 879 } 880 881 template <Direction DIR> 882 bool ExternalUnformattedIoStatementState<DIR>::Receive( 883 char *data, std::size_t bytes, std::size_t elementBytes) { 884 if constexpr (DIR == Direction::Output) { 885 this->Crash("ExternalUnformattedIoStatementState::Receive() called for " 886 "output statement"); 887 } 888 return this->unit().Receive(data, bytes, elementBytes, *this); 889 } 890 891 template <Direction DIR> 892 ChildIoStatementState<DIR>::ChildIoStatementState( 893 ChildIo &child, const char *sourceFile, int sourceLine) 894 : IoStatementBase{sourceFile, sourceLine}, child_{child} {} 895 896 template <Direction DIR> 897 MutableModes &ChildIoStatementState<DIR>::mutableModes() { 898 return child_.parent().mutableModes(); 899 } 900 901 template <Direction DIR> 902 ConnectionState &ChildIoStatementState<DIR>::GetConnectionState() { 903 return child_.parent().GetConnectionState(); 904 } 905 906 template <Direction DIR> 907 ExternalFileUnit *ChildIoStatementState<DIR>::GetExternalFileUnit() const { 908 return child_.parent().GetExternalFileUnit(); 909 } 910 911 template <Direction DIR> void ChildIoStatementState<DIR>::CompleteOperation() { 912 IoStatementBase::CompleteOperation(); 913 } 914 915 template <Direction DIR> int ChildIoStatementState<DIR>::EndIoStatement() { 916 CompleteOperation(); 917 auto result{IoStatementBase::EndIoStatement()}; 918 child_.EndIoStatement(); // annihilates *this in child_.u_ 919 return result; 920 } 921 922 template <Direction DIR> 923 bool ChildIoStatementState<DIR>::Emit( 924 const char *data, std::size_t bytes, std::size_t elementBytes) { 925 return child_.parent().Emit(data, bytes, elementBytes); 926 } 927 928 template <Direction DIR> 929 bool ChildIoStatementState<DIR>::Emit(const char *data, std::size_t bytes) { 930 return child_.parent().Emit(data, bytes); 931 } 932 933 template <Direction DIR> 934 bool ChildIoStatementState<DIR>::Emit(const char16_t *data, std::size_t chars) { 935 return child_.parent().Emit(data, chars); 936 } 937 938 template <Direction DIR> 939 bool ChildIoStatementState<DIR>::Emit(const char32_t *data, std::size_t chars) { 940 return child_.parent().Emit(data, chars); 941 } 942 943 template <Direction DIR> 944 std::size_t ChildIoStatementState<DIR>::GetNextInputBytes(const char *&p) { 945 return child_.parent().GetNextInputBytes(p); 946 } 947 948 template <Direction DIR> 949 void ChildIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) { 950 return child_.parent().HandleAbsolutePosition(n); 951 } 952 953 template <Direction DIR> 954 void ChildIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) { 955 return child_.parent().HandleRelativePosition(n); 956 } 957 958 template <Direction DIR, typename CHAR> 959 ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState( 960 ChildIo &child, const CHAR *format, std::size_t formatLength, 961 const char *sourceFile, int sourceLine) 962 : ChildIoStatementState<DIR>{child, sourceFile, sourceLine}, 963 mutableModes_{child.parent().mutableModes()}, format_{*this, format, 964 formatLength} {} 965 966 template <Direction DIR, typename CHAR> 967 void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() { 968 if (!this->completedOperation()) { 969 format_.Finish(*this); 970 ChildIoStatementState<DIR>::CompleteOperation(); 971 } 972 } 973 974 template <Direction DIR, typename CHAR> 975 int ChildFormattedIoStatementState<DIR, CHAR>::EndIoStatement() { 976 CompleteOperation(); 977 return ChildIoStatementState<DIR>::EndIoStatement(); 978 } 979 980 template <Direction DIR, typename CHAR> 981 bool ChildFormattedIoStatementState<DIR, CHAR>::AdvanceRecord(int) { 982 return false; // no can do in a child I/O 983 } 984 985 template <Direction DIR> 986 bool ChildUnformattedIoStatementState<DIR>::Receive( 987 char *data, std::size_t bytes, std::size_t elementBytes) { 988 return this->child().parent().Receive(data, bytes, elementBytes); 989 } 990 991 template class InternalIoStatementState<Direction::Output>; 992 template class InternalIoStatementState<Direction::Input>; 993 template class InternalFormattedIoStatementState<Direction::Output>; 994 template class InternalFormattedIoStatementState<Direction::Input>; 995 template class InternalListIoStatementState<Direction::Output>; 996 template class InternalListIoStatementState<Direction::Input>; 997 template class ExternalIoStatementState<Direction::Output>; 998 template class ExternalIoStatementState<Direction::Input>; 999 template class ExternalFormattedIoStatementState<Direction::Output>; 1000 template class ExternalFormattedIoStatementState<Direction::Input>; 1001 template class ExternalListIoStatementState<Direction::Output>; 1002 template class ExternalListIoStatementState<Direction::Input>; 1003 template class ExternalUnformattedIoStatementState<Direction::Output>; 1004 template class ExternalUnformattedIoStatementState<Direction::Input>; 1005 template class ChildIoStatementState<Direction::Output>; 1006 template class ChildIoStatementState<Direction::Input>; 1007 template class ChildFormattedIoStatementState<Direction::Output>; 1008 template class ChildFormattedIoStatementState<Direction::Input>; 1009 template class ChildListIoStatementState<Direction::Output>; 1010 template class ChildListIoStatementState<Direction::Input>; 1011 template class ChildUnformattedIoStatementState<Direction::Output>; 1012 template class ChildUnformattedIoStatementState<Direction::Input>; 1013 1014 void ExternalMiscIoStatementState::CompleteOperation() { 1015 if (completedOperation()) { 1016 return; 1017 } 1018 ExternalFileUnit &ext{unit()}; 1019 switch (which_) { 1020 case Flush: 1021 ext.FlushOutput(*this); 1022 std::fflush(nullptr); // flushes C stdio output streams (12.9(2)) 1023 break; 1024 case Backspace: 1025 ext.BackspaceRecord(*this); 1026 break; 1027 case Endfile: 1028 ext.Endfile(*this); 1029 break; 1030 case Rewind: 1031 ext.Rewind(*this); 1032 break; 1033 case Wait: 1034 break; // handled in io-api.cpp BeginWait 1035 } 1036 return IoStatementBase::CompleteOperation(); 1037 } 1038 1039 int ExternalMiscIoStatementState::EndIoStatement() { 1040 CompleteOperation(); 1041 return ExternalIoStatementBase::EndIoStatement(); 1042 } 1043 1044 InquireUnitState::InquireUnitState( 1045 ExternalFileUnit &unit, const char *sourceFile, int sourceLine) 1046 : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} 1047 1048 bool InquireUnitState::Inquire( 1049 InquiryKeywordHash inquiry, char *result, std::size_t length) { 1050 if (unit().createdForInternalChildIo()) { 1051 SignalError(IostatInquireInternalUnit, 1052 "INQUIRE of unit created for defined derived type I/O of an internal " 1053 "unit"); 1054 return false; 1055 } 1056 const char *str{nullptr}; 1057 switch (inquiry) { 1058 case HashInquiryKeyword("ACCESS"): 1059 if (!unit().IsConnected()) { 1060 str = "UNDEFINED"; 1061 } else { 1062 switch (unit().access) { 1063 case Access::Sequential: 1064 str = "SEQUENTIAL"; 1065 break; 1066 case Access::Direct: 1067 str = "DIRECT"; 1068 break; 1069 case Access::Stream: 1070 str = "STREAM"; 1071 break; 1072 } 1073 } 1074 break; 1075 case HashInquiryKeyword("ACTION"): 1076 str = !unit().IsConnected() ? "UNDEFINED" 1077 : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" 1078 : "READ"; 1079 break; 1080 case HashInquiryKeyword("ASYNCHRONOUS"): 1081 str = !unit().IsConnected() ? "UNDEFINED" 1082 : unit().mayAsynchronous() ? "YES" 1083 : "NO"; 1084 break; 1085 case HashInquiryKeyword("BLANK"): 1086 str = !unit().IsConnected() || unit().isUnformatted.value_or(true) 1087 ? "UNDEFINED" 1088 : unit().modes.editingFlags & blankZero ? "ZERO" 1089 : "NULL"; 1090 break; 1091 case HashInquiryKeyword("CARRIAGECONTROL"): 1092 str = "LIST"; 1093 break; 1094 case HashInquiryKeyword("CONVERT"): 1095 str = unit().swapEndianness() ? "SWAP" : "NATIVE"; 1096 break; 1097 case HashInquiryKeyword("DECIMAL"): 1098 str = !unit().IsConnected() || unit().isUnformatted.value_or(true) 1099 ? "UNDEFINED" 1100 : unit().modes.editingFlags & decimalComma ? "COMMA" 1101 : "POINT"; 1102 break; 1103 case HashInquiryKeyword("DELIM"): 1104 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) { 1105 str = "UNDEFINED"; 1106 } else { 1107 switch (unit().modes.delim) { 1108 case '\'': 1109 str = "APOSTROPHE"; 1110 break; 1111 case '"': 1112 str = "QUOTE"; 1113 break; 1114 default: 1115 str = "NONE"; 1116 break; 1117 } 1118 } 1119 break; 1120 case HashInquiryKeyword("DIRECT"): 1121 str = !unit().IsConnected() ? "UNKNOWN" 1122 : unit().access == Access::Direct || 1123 (unit().mayPosition() && unit().openRecl) 1124 ? "YES" 1125 : "NO"; 1126 break; 1127 case HashInquiryKeyword("ENCODING"): 1128 str = !unit().IsConnected() ? "UNKNOWN" 1129 : unit().isUnformatted.value_or(true) ? "UNDEFINED" 1130 : unit().isUTF8 ? "UTF-8" 1131 : "ASCII"; 1132 break; 1133 case HashInquiryKeyword("FORM"): 1134 str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED" 1135 : *unit().isUnformatted ? "UNFORMATTED" 1136 : "FORMATTED"; 1137 break; 1138 case HashInquiryKeyword("FORMATTED"): 1139 str = !unit().IsConnected() ? "UNDEFINED" 1140 : !unit().isUnformatted ? "UNKNOWN" 1141 : *unit().isUnformatted ? "NO" 1142 : "YES"; 1143 break; 1144 case HashInquiryKeyword("NAME"): 1145 str = unit().path(); 1146 if (!str) { 1147 return true; // result is undefined 1148 } 1149 break; 1150 case HashInquiryKeyword("PAD"): 1151 str = !unit().IsConnected() || unit().isUnformatted.value_or(true) 1152 ? "UNDEFINED" 1153 : unit().modes.pad ? "YES" 1154 : "NO"; 1155 break; 1156 case HashInquiryKeyword("POSITION"): 1157 if (!unit().IsConnected() || unit().access == Access::Direct) { 1158 str = "UNDEFINED"; 1159 } else { 1160 switch (unit().InquirePosition()) { 1161 case Position::Rewind: 1162 str = "REWIND"; 1163 break; 1164 case Position::Append: 1165 str = "APPEND"; 1166 break; 1167 case Position::AsIs: 1168 str = "ASIS"; 1169 break; 1170 } 1171 } 1172 break; 1173 case HashInquiryKeyword("READ"): 1174 str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO"; 1175 break; 1176 case HashInquiryKeyword("READWRITE"): 1177 str = !unit().IsConnected() ? "UNDEFINED" 1178 : unit().mayRead() && unit().mayWrite() ? "YES" 1179 : "NO"; 1180 break; 1181 case HashInquiryKeyword("ROUND"): 1182 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) { 1183 str = "UNDEFINED"; 1184 } else { 1185 switch (unit().modes.round) { 1186 case decimal::FortranRounding::RoundNearest: 1187 str = "NEAREST"; 1188 break; 1189 case decimal::FortranRounding::RoundUp: 1190 str = "UP"; 1191 break; 1192 case decimal::FortranRounding::RoundDown: 1193 str = "DOWN"; 1194 break; 1195 case decimal::FortranRounding::RoundToZero: 1196 str = "ZERO"; 1197 break; 1198 case decimal::FortranRounding::RoundCompatible: 1199 str = "COMPATIBLE"; 1200 break; 1201 } 1202 } 1203 break; 1204 case HashInquiryKeyword("SEQUENTIAL"): 1205 // "NO" for Direct, since Sequential would not work if 1206 // the unit were reopened without RECL=. 1207 str = !unit().IsConnected() ? "UNKNOWN" 1208 : unit().access == Access::Sequential ? "YES" 1209 : "NO"; 1210 break; 1211 case HashInquiryKeyword("SIGN"): 1212 str = !unit().IsConnected() || unit().isUnformatted.value_or(true) 1213 ? "UNDEFINED" 1214 : unit().modes.editingFlags & signPlus ? "PLUS" 1215 : "SUPPRESS"; 1216 break; 1217 case HashInquiryKeyword("STREAM"): 1218 str = !unit().IsConnected() ? "UNKNOWN" 1219 : unit().access == Access::Stream ? "YES" 1220 : "NO"; 1221 break; 1222 case HashInquiryKeyword("UNFORMATTED"): 1223 str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN" 1224 : *unit().isUnformatted ? "YES" 1225 : "NO"; 1226 break; 1227 case HashInquiryKeyword("WRITE"): 1228 str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO"; 1229 break; 1230 } 1231 if (str) { 1232 ToFortranDefaultCharacter(result, length, str); 1233 return true; 1234 } else { 1235 BadInquiryKeywordHashCrash(inquiry); 1236 return false; 1237 } 1238 } 1239 1240 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) { 1241 switch (inquiry) { 1242 case HashInquiryKeyword("EXIST"): 1243 result = true; 1244 return true; 1245 case HashInquiryKeyword("NAMED"): 1246 result = unit().path() != nullptr; 1247 return true; 1248 case HashInquiryKeyword("OPENED"): 1249 result = unit().IsConnected(); 1250 return true; 1251 case HashInquiryKeyword("PENDING"): 1252 result = false; // asynchronous I/O is not implemented 1253 return true; 1254 default: 1255 BadInquiryKeywordHashCrash(inquiry); 1256 return false; 1257 } 1258 } 1259 1260 bool InquireUnitState::Inquire( 1261 InquiryKeywordHash inquiry, std::int64_t, bool &result) { 1262 switch (inquiry) { 1263 case HashInquiryKeyword("PENDING"): 1264 result = false; // asynchronous I/O is not implemented 1265 return true; 1266 default: 1267 BadInquiryKeywordHashCrash(inquiry); 1268 return false; 1269 } 1270 } 1271 1272 bool InquireUnitState::Inquire( 1273 InquiryKeywordHash inquiry, std::int64_t &result) { 1274 switch (inquiry) { 1275 case HashInquiryKeyword("NEXTREC"): 1276 if (unit().access == Access::Direct) { 1277 result = unit().currentRecordNumber; 1278 } 1279 return true; 1280 case HashInquiryKeyword("NUMBER"): 1281 result = unit().unitNumber(); 1282 return true; 1283 case HashInquiryKeyword("POS"): 1284 result = unit().InquirePos(); 1285 return true; 1286 case HashInquiryKeyword("RECL"): 1287 if (!unit().IsConnected()) { 1288 result = -1; 1289 } else if (unit().access == Access::Stream) { 1290 result = -2; 1291 } else if (unit().openRecl) { 1292 result = *unit().openRecl; 1293 } else { 1294 result = std::numeric_limits<std::int32_t>::max(); 1295 } 1296 return true; 1297 case HashInquiryKeyword("SIZE"): 1298 result = -1; 1299 if (unit().IsConnected()) { 1300 if (auto size{unit().knownSize()}) { 1301 result = *size; 1302 } 1303 } 1304 return true; 1305 default: 1306 BadInquiryKeywordHashCrash(inquiry); 1307 return false; 1308 } 1309 } 1310 1311 InquireNoUnitState::InquireNoUnitState( 1312 const char *sourceFile, int sourceLine, int badUnitNumber) 1313 : NoUnitIoStatementState{*this, sourceFile, sourceLine, badUnitNumber} {} 1314 1315 bool InquireNoUnitState::Inquire( 1316 InquiryKeywordHash inquiry, char *result, std::size_t length) { 1317 switch (inquiry) { 1318 case HashInquiryKeyword("ACCESS"): 1319 case HashInquiryKeyword("ACTION"): 1320 case HashInquiryKeyword("ASYNCHRONOUS"): 1321 case HashInquiryKeyword("BLANK"): 1322 case HashInquiryKeyword("CARRIAGECONTROL"): 1323 case HashInquiryKeyword("CONVERT"): 1324 case HashInquiryKeyword("DECIMAL"): 1325 case HashInquiryKeyword("DELIM"): 1326 case HashInquiryKeyword("FORM"): 1327 case HashInquiryKeyword("NAME"): 1328 case HashInquiryKeyword("PAD"): 1329 case HashInquiryKeyword("POSITION"): 1330 case HashInquiryKeyword("ROUND"): 1331 case HashInquiryKeyword("SIGN"): 1332 ToFortranDefaultCharacter(result, length, "UNDEFINED"); 1333 return true; 1334 case HashInquiryKeyword("DIRECT"): 1335 case HashInquiryKeyword("ENCODING"): 1336 case HashInquiryKeyword("FORMATTED"): 1337 case HashInquiryKeyword("READ"): 1338 case HashInquiryKeyword("READWRITE"): 1339 case HashInquiryKeyword("SEQUENTIAL"): 1340 case HashInquiryKeyword("STREAM"): 1341 case HashInquiryKeyword("WRITE"): 1342 case HashInquiryKeyword("UNFORMATTED"): 1343 ToFortranDefaultCharacter(result, length, "UNKNOWN"); 1344 return true; 1345 default: 1346 BadInquiryKeywordHashCrash(inquiry); 1347 return false; 1348 } 1349 } 1350 1351 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) { 1352 switch (inquiry) { 1353 case HashInquiryKeyword("EXIST"): 1354 result = true; 1355 return true; 1356 case HashInquiryKeyword("NAMED"): 1357 case HashInquiryKeyword("OPENED"): 1358 case HashInquiryKeyword("PENDING"): 1359 result = false; 1360 return true; 1361 default: 1362 BadInquiryKeywordHashCrash(inquiry); 1363 return false; 1364 } 1365 } 1366 1367 bool InquireNoUnitState::Inquire( 1368 InquiryKeywordHash inquiry, std::int64_t, bool &result) { 1369 switch (inquiry) { 1370 case HashInquiryKeyword("PENDING"): 1371 result = false; 1372 return true; 1373 default: 1374 BadInquiryKeywordHashCrash(inquiry); 1375 return false; 1376 } 1377 } 1378 1379 bool InquireNoUnitState::Inquire( 1380 InquiryKeywordHash inquiry, std::int64_t &result) { 1381 switch (inquiry) { 1382 case HashInquiryKeyword("NUMBER"): 1383 result = badUnitNumber(); 1384 return true; 1385 case HashInquiryKeyword("NEXTREC"): 1386 case HashInquiryKeyword("POS"): 1387 case HashInquiryKeyword("RECL"): 1388 case HashInquiryKeyword("SIZE"): 1389 result = -1; 1390 return true; 1391 default: 1392 BadInquiryKeywordHashCrash(inquiry); 1393 return false; 1394 } 1395 } 1396 1397 InquireUnconnectedFileState::InquireUnconnectedFileState( 1398 OwningPtr<char> &&path, const char *sourceFile, int sourceLine) 1399 : NoUnitIoStatementState{*this, sourceFile, sourceLine}, path_{std::move( 1400 path)} {} 1401 1402 bool InquireUnconnectedFileState::Inquire( 1403 InquiryKeywordHash inquiry, char *result, std::size_t length) { 1404 const char *str{nullptr}; 1405 switch (inquiry) { 1406 case HashInquiryKeyword("ACCESS"): 1407 case HashInquiryKeyword("ACTION"): 1408 case HashInquiryKeyword("ASYNCHRONOUS"): 1409 case HashInquiryKeyword("BLANK"): 1410 case HashInquiryKeyword("CARRIAGECONTROL"): 1411 case HashInquiryKeyword("CONVERT"): 1412 case HashInquiryKeyword("DECIMAL"): 1413 case HashInquiryKeyword("DELIM"): 1414 case HashInquiryKeyword("FORM"): 1415 case HashInquiryKeyword("PAD"): 1416 case HashInquiryKeyword("POSITION"): 1417 case HashInquiryKeyword("ROUND"): 1418 case HashInquiryKeyword("SIGN"): 1419 str = "UNDEFINED"; 1420 break; 1421 case HashInquiryKeyword("DIRECT"): 1422 case HashInquiryKeyword("ENCODING"): 1423 case HashInquiryKeyword("FORMATTED"): 1424 case HashInquiryKeyword("SEQUENTIAL"): 1425 case HashInquiryKeyword("STREAM"): 1426 case HashInquiryKeyword("UNFORMATTED"): 1427 str = "UNKNONN"; 1428 break; 1429 case HashInquiryKeyword("READ"): 1430 str = MayRead(path_.get()) ? "YES" : "NO"; 1431 break; 1432 case HashInquiryKeyword("READWRITE"): 1433 str = MayReadAndWrite(path_.get()) ? "YES" : "NO"; 1434 break; 1435 case HashInquiryKeyword("WRITE"): 1436 str = MayWrite(path_.get()) ? "YES" : "NO"; 1437 break; 1438 case HashInquiryKeyword("NAME"): 1439 str = path_.get(); 1440 if (!str) { 1441 return true; // result is undefined 1442 } 1443 break; 1444 } 1445 if (str) { 1446 ToFortranDefaultCharacter(result, length, str); 1447 return true; 1448 } else { 1449 BadInquiryKeywordHashCrash(inquiry); 1450 return false; 1451 } 1452 } 1453 1454 bool InquireUnconnectedFileState::Inquire( 1455 InquiryKeywordHash inquiry, bool &result) { 1456 switch (inquiry) { 1457 case HashInquiryKeyword("EXIST"): 1458 result = IsExtant(path_.get()); 1459 return true; 1460 case HashInquiryKeyword("NAMED"): 1461 result = true; 1462 return true; 1463 case HashInquiryKeyword("OPENED"): 1464 result = false; 1465 return true; 1466 case HashInquiryKeyword("PENDING"): 1467 result = false; 1468 return true; 1469 default: 1470 BadInquiryKeywordHashCrash(inquiry); 1471 return false; 1472 } 1473 } 1474 1475 bool InquireUnconnectedFileState::Inquire( 1476 InquiryKeywordHash inquiry, std::int64_t, bool &result) { 1477 switch (inquiry) { 1478 case HashInquiryKeyword("PENDING"): 1479 result = false; 1480 return true; 1481 default: 1482 BadInquiryKeywordHashCrash(inquiry); 1483 return false; 1484 } 1485 } 1486 1487 bool InquireUnconnectedFileState::Inquire( 1488 InquiryKeywordHash inquiry, std::int64_t &result) { 1489 switch (inquiry) { 1490 case HashInquiryKeyword("NEXTREC"): 1491 case HashInquiryKeyword("NUMBER"): 1492 case HashInquiryKeyword("POS"): 1493 case HashInquiryKeyword("RECL"): 1494 result = -1; 1495 return true; 1496 case HashInquiryKeyword("SIZE"): 1497 result = SizeInBytes(path_.get()); 1498 return true; 1499 default: 1500 BadInquiryKeywordHashCrash(inquiry); 1501 return false; 1502 } 1503 } 1504 1505 InquireIOLengthState::InquireIOLengthState( 1506 const char *sourceFile, int sourceLine) 1507 : NoUnitIoStatementState{*this, sourceFile, sourceLine} {} 1508 1509 bool InquireIOLengthState::Emit(const char *, std::size_t n, std::size_t) { 1510 bytes_ += n; 1511 return true; 1512 } 1513 1514 bool InquireIOLengthState::Emit(const char *p, std::size_t n) { 1515 bytes_ += sizeof *p * n; 1516 return true; 1517 } 1518 1519 bool InquireIOLengthState::Emit(const char16_t *p, std::size_t n) { 1520 bytes_ += sizeof *p * n; 1521 return true; 1522 } 1523 1524 bool InquireIOLengthState::Emit(const char32_t *p, std::size_t n) { 1525 bytes_ += sizeof *p * n; 1526 return true; 1527 } 1528 1529 int ErroneousIoStatementState::EndIoStatement() { 1530 SignalPendingError(); 1531 if (unit_) { 1532 unit_->EndIoStatement(); 1533 } 1534 return IoStatementBase::EndIoStatement(); 1535 } 1536 1537 template bool IoStatementState::EmitEncoded<char>(const char *, std::size_t); 1538 template bool IoStatementState::EmitEncoded<char16_t>( 1539 const char16_t *, std::size_t); 1540 template bool IoStatementState::EmitEncoded<char32_t>( 1541 const char32_t *, std::size_t); 1542 1543 } // namespace Fortran::runtime::io 1544