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