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