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