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