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