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