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