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