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