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