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