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