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