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