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