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