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