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