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