1 //===-- runtime/io-api.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 // Implements the I/O statement API 10 11 #include "flang/Runtime/io-api.h" 12 #include "descriptor-io.h" 13 #include "edit-input.h" 14 #include "edit-output.h" 15 #include "environment.h" 16 #include "format.h" 17 #include "io-stmt.h" 18 #include "terminator.h" 19 #include "tools.h" 20 #include "unit.h" 21 #include "flang/Runtime/descriptor.h" 22 #include "flang/Runtime/memory.h" 23 #include <cstdlib> 24 #include <memory> 25 26 namespace Fortran::runtime::io { 27 28 const char *InquiryKeywordHashDecode( 29 char *buffer, std::size_t n, InquiryKeywordHash hash) { 30 if (n < 1) { 31 return nullptr; 32 } 33 char *p{buffer + n}; 34 *--p = '\0'; 35 while (hash > 1) { 36 if (p < buffer) { 37 return nullptr; 38 } 39 *--p = 'A' + (hash % 26); 40 hash /= 26; 41 } 42 return hash == 1 ? p : nullptr; 43 } 44 45 template <Direction DIR> 46 Cookie BeginInternalArrayListIO(const Descriptor &descriptor, 47 void ** /*scratchArea*/, std::size_t /*scratchBytes*/, 48 const char *sourceFile, int sourceLine) { 49 Terminator oom{sourceFile, sourceLine}; 50 return &New<InternalListIoStatementState<DIR>>{oom}( 51 descriptor, sourceFile, sourceLine) 52 .release() 53 ->ioStatementState(); 54 } 55 56 Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &descriptor, 57 void **scratchArea, std::size_t scratchBytes, const char *sourceFile, 58 int sourceLine) { 59 return BeginInternalArrayListIO<Direction::Output>( 60 descriptor, scratchArea, scratchBytes, sourceFile, sourceLine); 61 } 62 63 Cookie IONAME(BeginInternalArrayListInput)(const Descriptor &descriptor, 64 void **scratchArea, std::size_t scratchBytes, const char *sourceFile, 65 int sourceLine) { 66 return BeginInternalArrayListIO<Direction::Input>( 67 descriptor, scratchArea, scratchBytes, sourceFile, sourceLine); 68 } 69 70 template <Direction DIR> 71 Cookie BeginInternalArrayFormattedIO(const Descriptor &descriptor, 72 const char *format, std::size_t formatLength, void ** /*scratchArea*/, 73 std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { 74 Terminator oom{sourceFile, sourceLine}; 75 return &New<InternalFormattedIoStatementState<DIR>>{oom}( 76 descriptor, format, formatLength, sourceFile, sourceLine) 77 .release() 78 ->ioStatementState(); 79 } 80 81 Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor, 82 const char *format, std::size_t formatLength, void **scratchArea, 83 std::size_t scratchBytes, const char *sourceFile, int sourceLine) { 84 return BeginInternalArrayFormattedIO<Direction::Output>(descriptor, format, 85 formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); 86 } 87 88 Cookie IONAME(BeginInternalArrayFormattedInput)(const Descriptor &descriptor, 89 const char *format, std::size_t formatLength, void **scratchArea, 90 std::size_t scratchBytes, const char *sourceFile, int sourceLine) { 91 return BeginInternalArrayFormattedIO<Direction::Input>(descriptor, format, 92 formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); 93 } 94 95 template <Direction DIR> 96 Cookie BeginInternalListIO( 97 std::conditional_t<DIR == Direction::Input, const char, char> *internal, 98 std::size_t internalLength, void ** /*scratchArea*/, 99 std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) { 100 Terminator oom{sourceFile, sourceLine}; 101 return &New<InternalListIoStatementState<DIR>>{oom}( 102 internal, internalLength, sourceFile, sourceLine) 103 .release() 104 ->ioStatementState(); 105 } 106 107 Cookie IONAME(BeginInternalListOutput)(char *internal, 108 std::size_t internalLength, void **scratchArea, std::size_t scratchBytes, 109 const char *sourceFile, int sourceLine) { 110 return BeginInternalListIO<Direction::Output>(internal, internalLength, 111 scratchArea, scratchBytes, sourceFile, sourceLine); 112 } 113 114 Cookie IONAME(BeginInternalListInput)(const char *internal, 115 std::size_t internalLength, void **scratchArea, std::size_t scratchBytes, 116 const char *sourceFile, int sourceLine) { 117 return BeginInternalListIO<Direction::Input>(internal, internalLength, 118 scratchArea, scratchBytes, sourceFile, sourceLine); 119 } 120 121 template <Direction DIR> 122 Cookie BeginInternalFormattedIO( 123 std::conditional_t<DIR == Direction::Input, const char, char> *internal, 124 std::size_t internalLength, const char *format, std::size_t formatLength, 125 void ** /*scratchArea*/, std::size_t /*scratchBytes*/, 126 const char *sourceFile, int sourceLine) { 127 Terminator oom{sourceFile, sourceLine}; 128 return &New<InternalFormattedIoStatementState<DIR>>{oom}( 129 internal, internalLength, format, formatLength, sourceFile, sourceLine) 130 .release() 131 ->ioStatementState(); 132 } 133 134 Cookie IONAME(BeginInternalFormattedOutput)(char *internal, 135 std::size_t internalLength, const char *format, std::size_t formatLength, 136 void **scratchArea, std::size_t scratchBytes, const char *sourceFile, 137 int sourceLine) { 138 return BeginInternalFormattedIO<Direction::Output>(internal, internalLength, 139 format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); 140 } 141 142 Cookie IONAME(BeginInternalFormattedInput)(const char *internal, 143 std::size_t internalLength, const char *format, std::size_t formatLength, 144 void **scratchArea, std::size_t scratchBytes, const char *sourceFile, 145 int sourceLine) { 146 return BeginInternalFormattedIO<Direction::Input>(internal, internalLength, 147 format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); 148 } 149 150 template <Direction DIR, template <Direction> class STATE, typename... A> 151 Cookie BeginExternalListIO(const char *what, int unitNumber, 152 const char *sourceFile, int sourceLine, A &&...xs) { 153 Terminator terminator{sourceFile, sourceLine}; 154 if (unitNumber == DefaultUnit) { 155 unitNumber = DIR == Direction::Input ? 5 : 6; 156 } 157 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 158 unitNumber, DIR, false /*!unformatted*/, terminator)}; 159 if (ChildIo * child{unit.GetChildIo()}) { 160 return child->CheckFormattingAndDirection(terminator, what, false, DIR) 161 ? &child->BeginIoStatement<ChildListIoStatementState<DIR>>( 162 *child, sourceFile, sourceLine) 163 : nullptr; 164 } else { 165 if (unit.access == Access::Direct) { 166 terminator.Crash("%s attempted on direct access file", what); 167 return nullptr; 168 } 169 if (!unit.isUnformatted.has_value()) { 170 unit.isUnformatted = false; 171 } 172 if (*unit.isUnformatted) { 173 terminator.Crash("%s attempted on unformatted file", what); 174 return nullptr; 175 } 176 IoErrorHandler handler{terminator}; 177 unit.SetDirection(DIR, handler); 178 IoStatementState &io{unit.BeginIoStatement<STATE<DIR>>( 179 std::forward<A>(xs)..., unit, sourceFile, sourceLine)}; 180 return &io; 181 } 182 } 183 184 Cookie IONAME(BeginExternalListOutput)( 185 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 186 return BeginExternalListIO<Direction::Output, ExternalListIoStatementState>( 187 "List-directed output", unitNumber, sourceFile, sourceLine); 188 } 189 190 Cookie IONAME(BeginExternalListInput)( 191 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 192 return BeginExternalListIO<Direction::Input, ExternalListIoStatementState>( 193 "List-directed input", unitNumber, sourceFile, sourceLine); 194 } 195 196 template <Direction DIR> 197 Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength, 198 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 199 Terminator terminator{sourceFile, sourceLine}; 200 if (unitNumber == DefaultUnit) { 201 unitNumber = DIR == Direction::Input ? 5 : 6; 202 } 203 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 204 unitNumber, DIR, false /*!unformatted*/, terminator)}; 205 if (ChildIo * child{unit.GetChildIo()}) { 206 return child->CheckFormattingAndDirection(terminator, 207 DIR == Direction::Output ? "formatted output" 208 : "formatted input", 209 false, DIR) 210 ? &child->BeginIoStatement<ChildFormattedIoStatementState<DIR>>( 211 *child, sourceFile, sourceLine) 212 : nullptr; 213 } else { 214 if (!unit.isUnformatted.has_value()) { 215 unit.isUnformatted = false; 216 } 217 if (*unit.isUnformatted) { 218 terminator.Crash("Formatted I/O attempted on unformatted file"); 219 return nullptr; 220 } 221 IoErrorHandler handler{terminator}; 222 unit.SetDirection(DIR, handler); 223 IoStatementState &io{ 224 unit.BeginIoStatement<ExternalFormattedIoStatementState<DIR>>( 225 unit, format, formatLength, sourceFile, sourceLine)}; 226 return &io; 227 } 228 } 229 230 Cookie IONAME(BeginExternalFormattedOutput)(const char *format, 231 std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, 232 int sourceLine) { 233 return BeginExternalFormattedIO<Direction::Output>( 234 format, formatLength, unitNumber, sourceFile, sourceLine); 235 } 236 237 Cookie IONAME(BeginExternalFormattedInput)(const char *format, 238 std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, 239 int sourceLine) { 240 return BeginExternalFormattedIO<Direction::Input>( 241 format, formatLength, unitNumber, sourceFile, sourceLine); 242 } 243 244 template <Direction DIR> 245 Cookie BeginUnformattedIO( 246 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 247 Terminator terminator{sourceFile, sourceLine}; 248 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 249 unitNumber, DIR, true /*unformatted*/, terminator)}; 250 if (ChildIo * child{unit.GetChildIo()}) { 251 return child->CheckFormattingAndDirection(terminator, 252 DIR == Direction::Output ? "unformatted output" 253 : "unformatted input", 254 true, DIR) 255 ? &child->BeginIoStatement<ChildUnformattedIoStatementState<DIR>>( 256 *child, sourceFile, sourceLine) 257 : nullptr; 258 } else { 259 if (!unit.isUnformatted.has_value()) { 260 unit.isUnformatted = true; 261 } 262 if (!*unit.isUnformatted) { 263 terminator.Crash("Unformatted I/O attempted on formatted file"); 264 } 265 IoStatementState &io{ 266 unit.BeginIoStatement<ExternalUnformattedIoStatementState<DIR>>( 267 unit, sourceFile, sourceLine)}; 268 IoErrorHandler handler{terminator}; 269 unit.SetDirection(DIR, handler); 270 if constexpr (DIR == Direction::Output) { 271 if (unit.access == Access::Sequential) { 272 // Create space for (sub)record header to be completed by 273 // ExternalFileUnit::AdvanceRecord() 274 unit.recordLength.reset(); // in case of prior BACKSPACE 275 io.Emit("\0\0\0\0", 4); // placeholder for record length header 276 } 277 } 278 return &io; 279 } 280 } 281 282 Cookie IONAME(BeginUnformattedOutput)( 283 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 284 return BeginUnformattedIO<Direction::Output>( 285 unitNumber, sourceFile, sourceLine); 286 } 287 288 Cookie IONAME(BeginUnformattedInput)( 289 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 290 return BeginUnformattedIO<Direction::Input>( 291 unitNumber, sourceFile, sourceLine); 292 } 293 294 Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=) 295 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 296 bool wasExtant{false}; 297 Terminator terminator{sourceFile, sourceLine}; 298 ExternalFileUnit &unit{ 299 ExternalFileUnit::LookUpOrCreate(unitNumber, terminator, wasExtant)}; 300 return &unit.BeginIoStatement<OpenStatementState>( 301 unit, wasExtant, sourceFile, sourceLine); 302 } 303 304 Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) 305 const char *sourceFile, int sourceLine) { 306 Terminator terminator{sourceFile, sourceLine}; 307 ExternalFileUnit &unit{ExternalFileUnit::NewUnit(terminator)}; 308 return &unit.BeginIoStatement<OpenStatementState>( 309 unit, false /*was an existing file*/, sourceFile, sourceLine); 310 } 311 312 Cookie IONAME(BeginClose)( 313 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 314 if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) { 315 return &unit->BeginIoStatement<CloseStatementState>( 316 *unit, sourceFile, sourceLine); 317 } else { 318 // CLOSE(UNIT=bad unit) is just a no-op 319 Terminator oom{sourceFile, sourceLine}; 320 return &New<NoopCloseStatementState>{oom}(sourceFile, sourceLine) 321 .release() 322 ->ioStatementState(); 323 } 324 } 325 326 Cookie IONAME(BeginFlush)( 327 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 328 Terminator terminator{sourceFile, sourceLine}; 329 ExternalFileUnit &unit{ 330 ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; 331 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 332 unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine); 333 } 334 335 Cookie IONAME(BeginBackspace)( 336 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 337 Terminator terminator{sourceFile, sourceLine}; 338 ExternalFileUnit &unit{ 339 ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; 340 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 341 unit, ExternalMiscIoStatementState::Backspace, sourceFile, sourceLine); 342 } 343 344 Cookie IONAME(BeginEndfile)( 345 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 346 Terminator terminator{sourceFile, sourceLine}; 347 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 348 unitNumber, Direction::Output, std::nullopt, terminator)}; 349 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 350 unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine); 351 } 352 353 Cookie IONAME(BeginRewind)( 354 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 355 Terminator terminator{sourceFile, sourceLine}; 356 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 357 unitNumber, Direction::Input, std::nullopt, terminator)}; 358 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 359 unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine); 360 } 361 362 Cookie IONAME(BeginInquireUnit)( 363 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 364 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 365 return &unit->BeginIoStatement<InquireUnitState>( 366 *unit, sourceFile, sourceLine); 367 } else { 368 // INQUIRE(UNIT=unrecognized unit) 369 Terminator oom{sourceFile, sourceLine}; 370 return &New<InquireNoUnitState>{oom}(sourceFile, sourceLine) 371 .release() 372 ->ioStatementState(); 373 } 374 } 375 376 Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength, 377 const char *sourceFile, int sourceLine) { 378 Terminator oom{sourceFile, sourceLine}; 379 auto trimmed{ 380 SaveDefaultCharacter(path, TrimTrailingSpaces(path, pathLength), oom)}; 381 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(trimmed.get())}) { 382 // INQUIRE(FILE=) to a connected unit 383 return &unit->BeginIoStatement<InquireUnitState>( 384 *unit, sourceFile, sourceLine); 385 } else { 386 return &New<InquireUnconnectedFileState>{oom}( 387 std::move(trimmed), sourceFile, sourceLine) 388 .release() 389 ->ioStatementState(); 390 } 391 } 392 393 Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) { 394 Terminator oom{sourceFile, sourceLine}; 395 return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine) 396 .release() 397 ->ioStatementState(); 398 } 399 400 // Control list items 401 402 void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr, 403 bool hasEnd, bool hasEor, bool hasIoMsg) { 404 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 405 if (hasIoStat) { 406 handler.HasIoStat(); 407 } 408 if (hasErr) { 409 handler.HasErrLabel(); 410 } 411 if (hasEnd) { 412 handler.HasEndLabel(); 413 } 414 if (hasEor) { 415 handler.HasEorLabel(); 416 } 417 if (hasIoMsg) { 418 handler.HasIoMsg(); 419 } 420 } 421 422 static bool YesOrNo(const char *keyword, std::size_t length, const char *what, 423 IoErrorHandler &handler) { 424 static const char *keywords[]{"YES", "NO", nullptr}; 425 switch (IdentifyValue(keyword, length, keywords)) { 426 case 0: 427 return true; 428 case 1: 429 return false; 430 default: 431 handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what, 432 static_cast<int>(length), keyword); 433 return false; 434 } 435 } 436 437 bool IONAME(SetAdvance)( 438 Cookie cookie, const char *keyword, std::size_t length) { 439 IoStatementState &io{*cookie}; 440 bool nonAdvancing{ 441 !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler())}; 442 if (nonAdvancing && io.GetConnectionState().access == Access::Direct) { 443 io.GetIoErrorHandler().SignalError( 444 "Non-advancing I/O attempted on direct access file"); 445 } else { 446 io.mutableModes().nonAdvancing = nonAdvancing; 447 } 448 return true; 449 } 450 451 bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { 452 IoStatementState &io{*cookie}; 453 ConnectionState &connection{io.GetConnectionState()}; 454 static const char *keywords[]{"NULL", "ZERO", nullptr}; 455 switch (IdentifyValue(keyword, length, keywords)) { 456 case 0: 457 connection.modes.editingFlags &= ~blankZero; 458 return true; 459 case 1: 460 connection.modes.editingFlags |= blankZero; 461 return true; 462 default: 463 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 464 "Invalid BLANK='%.*s'", static_cast<int>(length), keyword); 465 return false; 466 } 467 } 468 469 bool IONAME(SetDecimal)( 470 Cookie cookie, const char *keyword, std::size_t length) { 471 IoStatementState &io{*cookie}; 472 ConnectionState &connection{io.GetConnectionState()}; 473 static const char *keywords[]{"COMMA", "POINT", nullptr}; 474 switch (IdentifyValue(keyword, length, keywords)) { 475 case 0: 476 connection.modes.editingFlags |= decimalComma; 477 return true; 478 case 1: 479 connection.modes.editingFlags &= ~decimalComma; 480 return true; 481 default: 482 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 483 "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword); 484 return false; 485 } 486 } 487 488 bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) { 489 IoStatementState &io{*cookie}; 490 ConnectionState &connection{io.GetConnectionState()}; 491 static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr}; 492 switch (IdentifyValue(keyword, length, keywords)) { 493 case 0: 494 connection.modes.delim = '\''; 495 return true; 496 case 1: 497 connection.modes.delim = '"'; 498 return true; 499 case 2: 500 connection.modes.delim = '\0'; 501 return true; 502 default: 503 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 504 "Invalid DELIM='%.*s'", static_cast<int>(length), keyword); 505 return false; 506 } 507 } 508 509 bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { 510 IoStatementState &io{*cookie}; 511 ConnectionState &connection{io.GetConnectionState()}; 512 connection.modes.pad = 513 YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler()); 514 return true; 515 } 516 517 bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) { 518 IoStatementState &io{*cookie}; 519 ConnectionState &connection{io.GetConnectionState()}; 520 if (connection.access != Access::Stream) { 521 io.GetIoErrorHandler().SignalError( 522 "REC= may not appear unless ACCESS='STREAM'"); 523 return false; 524 } 525 if (pos < 1) { 526 io.GetIoErrorHandler().SignalError( 527 "POS=%zd is invalid", static_cast<std::intmax_t>(pos)); 528 return false; 529 } 530 if (auto *unit{io.GetExternalFileUnit()}) { 531 unit->SetPosition(pos); 532 return true; 533 } 534 io.GetIoErrorHandler().Crash("SetPos() on internal unit"); 535 return false; 536 } 537 538 bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) { 539 IoStatementState &io{*cookie}; 540 ConnectionState &connection{io.GetConnectionState()}; 541 if (connection.access != Access::Direct) { 542 io.GetIoErrorHandler().SignalError( 543 "REC= may not appear unless ACCESS='DIRECT'"); 544 return false; 545 } 546 if (!connection.isFixedRecordLength || !connection.recordLength) { 547 io.GetIoErrorHandler().SignalError("RECL= was not specified"); 548 return false; 549 } 550 if (rec < 1) { 551 io.GetIoErrorHandler().SignalError( 552 "REC=%zd is invalid", static_cast<std::intmax_t>(rec)); 553 return false; 554 } 555 connection.currentRecordNumber = rec; 556 if (auto *unit{io.GetExternalFileUnit()}) { 557 unit->SetPosition((rec - 1) * *connection.recordLength); 558 } 559 return true; 560 } 561 562 bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { 563 IoStatementState &io{*cookie}; 564 ConnectionState &connection{io.GetConnectionState()}; 565 static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE", 566 "PROCESSOR_DEFINED", nullptr}; 567 switch (IdentifyValue(keyword, length, keywords)) { 568 case 0: 569 connection.modes.round = decimal::RoundUp; 570 return true; 571 case 1: 572 connection.modes.round = decimal::RoundDown; 573 return true; 574 case 2: 575 connection.modes.round = decimal::RoundToZero; 576 return true; 577 case 3: 578 connection.modes.round = decimal::RoundNearest; 579 return true; 580 case 4: 581 connection.modes.round = decimal::RoundCompatible; 582 return true; 583 case 5: 584 connection.modes.round = executionEnvironment.defaultOutputRoundingMode; 585 return true; 586 default: 587 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 588 "Invalid ROUND='%.*s'", static_cast<int>(length), keyword); 589 return false; 590 } 591 } 592 593 bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) { 594 IoStatementState &io{*cookie}; 595 ConnectionState &connection{io.GetConnectionState()}; 596 static const char *keywords[]{"PLUS", "YES", "PROCESSOR_DEFINED", nullptr}; 597 switch (IdentifyValue(keyword, length, keywords)) { 598 case 0: 599 connection.modes.editingFlags |= signPlus; 600 return true; 601 case 1: 602 case 2: // processor default is SS 603 connection.modes.editingFlags &= ~signPlus; 604 return true; 605 default: 606 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 607 "Invalid SIGN='%.*s'", static_cast<int>(length), keyword); 608 return false; 609 } 610 } 611 612 bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) { 613 IoStatementState &io{*cookie}; 614 auto *open{io.get_if<OpenStatementState>()}; 615 if (!open) { 616 io.GetIoErrorHandler().Crash( 617 "SetAccess() called when not in an OPEN statement"); 618 } 619 static const char *keywords[]{ 620 "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr}; 621 switch (IdentifyValue(keyword, length, keywords)) { 622 case 0: 623 open->set_access(Access::Sequential); 624 break; 625 case 1: 626 open->set_access(Access::Direct); 627 break; 628 case 2: 629 open->set_access(Access::Stream); 630 break; 631 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND 632 open->set_position(Position::Append); 633 break; 634 default: 635 open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'", 636 static_cast<int>(length), keyword); 637 } 638 return true; 639 } 640 641 bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) { 642 IoStatementState &io{*cookie}; 643 auto *open{io.get_if<OpenStatementState>()}; 644 if (!open) { 645 io.GetIoErrorHandler().Crash( 646 "SetAction() called when not in an OPEN statement"); 647 } 648 std::optional<Action> action; 649 static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr}; 650 switch (IdentifyValue(keyword, length, keywords)) { 651 case 0: 652 action = Action::Read; 653 break; 654 case 1: 655 action = Action::Write; 656 break; 657 case 2: 658 action = Action::ReadWrite; 659 break; 660 default: 661 open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'", 662 static_cast<int>(length), keyword); 663 return false; 664 } 665 RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value()); 666 if (open->wasExtant()) { 667 if ((*action != Action::Write) != open->unit().mayRead() || 668 (*action != Action::Read) != open->unit().mayWrite()) { 669 open->SignalError("ACTION= may not be changed on an open unit"); 670 } 671 } 672 open->set_action(*action); 673 return true; 674 } 675 676 bool IONAME(SetAsynchronous)( 677 Cookie cookie, const char *keyword, std::size_t length) { 678 IoStatementState &io{*cookie}; 679 auto *open{io.get_if<OpenStatementState>()}; 680 if (!open) { 681 io.GetIoErrorHandler().Crash( 682 "SetAsynchronous() called when not in an OPEN statement"); 683 } 684 static const char *keywords[]{"YES", "NO", nullptr}; 685 switch (IdentifyValue(keyword, length, keywords)) { 686 case 0: 687 open->unit().set_mayAsynchronous(true); 688 return true; 689 case 1: 690 open->unit().set_mayAsynchronous(false); 691 return true; 692 default: 693 open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'", 694 static_cast<int>(length), keyword); 695 return false; 696 } 697 } 698 699 bool IONAME(SetCarriagecontrol)( 700 Cookie cookie, const char *keyword, std::size_t length) { 701 IoStatementState &io{*cookie}; 702 auto *open{io.get_if<OpenStatementState>()}; 703 if (!open) { 704 io.GetIoErrorHandler().Crash( 705 "SetCarriageControl() called when not in an OPEN statement"); 706 } 707 static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr}; 708 switch (IdentifyValue(keyword, length, keywords)) { 709 case 0: 710 return true; 711 case 1: 712 case 2: 713 open->SignalError(IostatErrorInKeyword, 714 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length), 715 keyword); 716 return false; 717 default: 718 open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'", 719 static_cast<int>(length), keyword); 720 return false; 721 } 722 } 723 724 bool IONAME(SetConvert)( 725 Cookie cookie, const char *keyword, std::size_t length) { 726 IoStatementState &io{*cookie}; 727 auto *open{io.get_if<OpenStatementState>()}; 728 if (!open) { 729 io.GetIoErrorHandler().Crash( 730 "SetConvert() called when not in an OPEN statement"); 731 } 732 if (auto convert{GetConvertFromString(keyword, length)}) { 733 open->set_convert(*convert); 734 return true; 735 } else { 736 open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'", 737 static_cast<int>(length), keyword); 738 return false; 739 } 740 } 741 742 bool IONAME(SetEncoding)( 743 Cookie cookie, const char *keyword, std::size_t length) { 744 IoStatementState &io{*cookie}; 745 auto *open{io.get_if<OpenStatementState>()}; 746 if (!open) { 747 io.GetIoErrorHandler().Crash( 748 "SetEncoding() called when not in an OPEN statement"); 749 } 750 bool isUTF8{false}; 751 static const char *keywords[]{"UTF-8", "DEFAULT", nullptr}; 752 switch (IdentifyValue(keyword, length, keywords)) { 753 case 0: 754 isUTF8 = true; 755 break; 756 case 1: 757 isUTF8 = false; 758 break; 759 default: 760 open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'", 761 static_cast<int>(length), keyword); 762 } 763 if (isUTF8 != open->unit().isUTF8) { 764 if (open->wasExtant()) { 765 open->SignalError("ENCODING= may not be changed on an open unit"); 766 } 767 open->unit().isUTF8 = isUTF8; 768 } 769 return true; 770 } 771 772 bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) { 773 IoStatementState &io{*cookie}; 774 auto *open{io.get_if<OpenStatementState>()}; 775 if (!open) { 776 io.GetIoErrorHandler().Crash( 777 "SetForm() called when not in an OPEN statement"); 778 } 779 static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr}; 780 switch (IdentifyValue(keyword, length, keywords)) { 781 case 0: 782 open->set_isUnformatted(false); 783 break; 784 case 1: 785 open->set_isUnformatted(true); 786 break; 787 default: 788 open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'", 789 static_cast<int>(length), keyword); 790 } 791 return true; 792 } 793 794 bool IONAME(SetPosition)( 795 Cookie cookie, const char *keyword, std::size_t length) { 796 IoStatementState &io{*cookie}; 797 auto *open{io.get_if<OpenStatementState>()}; 798 if (!open) { 799 io.GetIoErrorHandler().Crash( 800 "SetPosition() called when not in an OPEN statement"); 801 } 802 static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr}; 803 switch (IdentifyValue(keyword, length, positions)) { 804 case 0: 805 open->set_position(Position::AsIs); 806 return true; 807 case 1: 808 open->set_position(Position::Rewind); 809 return true; 810 case 2: 811 open->set_position(Position::Append); 812 return true; 813 default: 814 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 815 "Invalid POSITION='%.*s'", static_cast<int>(length), keyword); 816 } 817 return true; 818 } 819 820 bool IONAME(SetRecl)(Cookie cookie, std::size_t n) { 821 IoStatementState &io{*cookie}; 822 auto *open{io.get_if<OpenStatementState>()}; 823 if (!open) { 824 io.GetIoErrorHandler().Crash( 825 "SetRecl() called when not in an OPEN statement"); 826 } 827 if (n <= 0) { 828 io.GetIoErrorHandler().SignalError("RECL= must be greater than zero"); 829 } 830 if (open->wasExtant() && open->unit().isFixedRecordLength && 831 open->unit().recordLength.value_or(n) != static_cast<std::int64_t>(n)) { 832 open->SignalError("RECL= may not be changed for an open unit"); 833 } 834 open->unit().isFixedRecordLength = true; 835 open->unit().recordLength = n; 836 return true; 837 } 838 839 bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { 840 IoStatementState &io{*cookie}; 841 if (auto *open{io.get_if<OpenStatementState>()}) { 842 static const char *statuses[]{ 843 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; 844 switch (IdentifyValue(keyword, length, statuses)) { 845 case 0: 846 open->set_status(OpenStatus::Old); 847 return true; 848 case 1: 849 open->set_status(OpenStatus::New); 850 return true; 851 case 2: 852 open->set_status(OpenStatus::Scratch); 853 return true; 854 case 3: 855 open->set_status(OpenStatus::Replace); 856 return true; 857 case 4: 858 open->set_status(OpenStatus::Unknown); 859 return true; 860 default: 861 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 862 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 863 } 864 return false; 865 } 866 if (auto *close{io.get_if<CloseStatementState>()}) { 867 static const char *statuses[]{"KEEP", "DELETE", nullptr}; 868 switch (IdentifyValue(keyword, length, statuses)) { 869 case 0: 870 close->set_status(CloseStatus::Keep); 871 return true; 872 case 1: 873 close->set_status(CloseStatus::Delete); 874 return true; 875 default: 876 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 877 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 878 } 879 return false; 880 } 881 if (io.get_if<NoopCloseStatementState>()) { 882 return true; // don't bother validating STATUS= in a no-op CLOSE 883 } 884 io.GetIoErrorHandler().Crash( 885 "SetStatus() called when not in an OPEN or CLOSE statement"); 886 } 887 888 bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) { 889 IoStatementState &io{*cookie}; 890 if (auto *open{io.get_if<OpenStatementState>()}) { 891 open->set_path(path, chars); 892 return true; 893 } 894 io.GetIoErrorHandler().Crash( 895 "SetFile() called when not in an OPEN statement"); 896 return false; 897 } 898 899 bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) { 900 IoStatementState &io{*cookie}; 901 auto *open{io.get_if<OpenStatementState>()}; 902 if (!open) { 903 io.GetIoErrorHandler().Crash( 904 "GetNewUnit() called when not in an OPEN statement"); 905 } 906 if (!SetInteger(unit, kind, open->unit().unitNumber())) { 907 open->SignalError("GetNewUnit(): Bad INTEGER kind(%d) for result"); 908 } 909 return true; 910 } 911 912 // Data transfers 913 914 bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 915 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 916 } 917 918 bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 919 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 920 } 921 922 bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x, 923 std::size_t length, std::size_t elementBytes) { 924 IoStatementState &io{*cookie}; 925 if (auto *unf{io.get_if< 926 ExternalUnformattedIoStatementState<Direction::Output>>()}) { 927 return unf->Emit(x, length, elementBytes); 928 } else if (auto *inq{io.get_if<InquireIOLengthState>()}) { 929 return inq->Emit(x, length, elementBytes); 930 } 931 io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O " 932 "statement that is not unformatted output"); 933 return false; 934 } 935 936 bool IONAME(InputUnformattedBlock)( 937 Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) { 938 IoStatementState &io{*cookie}; 939 io.BeginReadingRecord(); 940 if (io.GetIoErrorHandler().InError()) { 941 return false; 942 } 943 if (auto *unf{ 944 io.get_if<ExternalUnformattedIoStatementState<Direction::Input>>()}) { 945 return unf->Receive(x, length, elementBytes); 946 } 947 io.GetIoErrorHandler().Crash("InputUnformattedBlock() called for an I/O " 948 "statement that is not unformatted output"); 949 return false; 950 } 951 952 bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) { 953 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger8"); 954 StaticDescriptor staticDescriptor; 955 Descriptor &descriptor{staticDescriptor.descriptor()}; 956 descriptor.Establish( 957 TypeCategory::Integer, 1, reinterpret_cast<void *>(&n), 0); 958 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 959 } 960 961 bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) { 962 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger16"); 963 StaticDescriptor staticDescriptor; 964 Descriptor &descriptor{staticDescriptor.descriptor()}; 965 descriptor.Establish( 966 TypeCategory::Integer, 2, reinterpret_cast<void *>(&n), 0); 967 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 968 } 969 970 bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) { 971 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger32"); 972 StaticDescriptor staticDescriptor; 973 Descriptor &descriptor{staticDescriptor.descriptor()}; 974 descriptor.Establish( 975 TypeCategory::Integer, 4, reinterpret_cast<void *>(&n), 0); 976 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 977 } 978 979 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { 980 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64"); 981 StaticDescriptor staticDescriptor; 982 Descriptor &descriptor{staticDescriptor.descriptor()}; 983 descriptor.Establish( 984 TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0); 985 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 986 } 987 988 #ifdef __SIZEOF_INT128__ 989 bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) { 990 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger128"); 991 StaticDescriptor staticDescriptor; 992 Descriptor &descriptor{staticDescriptor.descriptor()}; 993 descriptor.Establish( 994 TypeCategory::Integer, 16, reinterpret_cast<void *>(&n), 0); 995 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 996 } 997 #endif 998 999 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { 1000 cookie->CheckFormattedStmtType<Direction::Input>("InputInteger"); 1001 StaticDescriptor staticDescriptor; 1002 Descriptor &descriptor{staticDescriptor.descriptor()}; 1003 descriptor.Establish( 1004 TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0); 1005 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1006 } 1007 1008 bool IONAME(OutputReal32)(Cookie cookie, float x) { 1009 cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32"); 1010 StaticDescriptor staticDescriptor; 1011 Descriptor &descriptor{staticDescriptor.descriptor()}; 1012 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 1013 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1014 } 1015 1016 bool IONAME(OutputReal64)(Cookie cookie, double x) { 1017 cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64"); 1018 StaticDescriptor staticDescriptor; 1019 Descriptor &descriptor{staticDescriptor.descriptor()}; 1020 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 1021 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1022 } 1023 1024 bool IONAME(InputReal32)(Cookie cookie, float &x) { 1025 cookie->CheckFormattedStmtType<Direction::Input>("InputReal32"); 1026 StaticDescriptor staticDescriptor; 1027 Descriptor &descriptor{staticDescriptor.descriptor()}; 1028 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 1029 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1030 } 1031 1032 bool IONAME(InputReal64)(Cookie cookie, double &x) { 1033 cookie->CheckFormattedStmtType<Direction::Input>("InputReal64"); 1034 StaticDescriptor staticDescriptor; 1035 Descriptor &descriptor{staticDescriptor.descriptor()}; 1036 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 1037 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1038 } 1039 1040 bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) { 1041 cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32"); 1042 float z[2]{r, i}; 1043 StaticDescriptor staticDescriptor; 1044 Descriptor &descriptor{staticDescriptor.descriptor()}; 1045 descriptor.Establish( 1046 TypeCategory::Complex, 4, reinterpret_cast<void *>(&z), 0); 1047 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1048 } 1049 1050 bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) { 1051 cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64"); 1052 double z[2]{r, i}; 1053 StaticDescriptor staticDescriptor; 1054 Descriptor &descriptor{staticDescriptor.descriptor()}; 1055 descriptor.Establish( 1056 TypeCategory::Complex, 8, reinterpret_cast<void *>(&z), 0); 1057 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1058 } 1059 1060 bool IONAME(InputComplex32)(Cookie cookie, float z[2]) { 1061 cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32"); 1062 StaticDescriptor staticDescriptor; 1063 Descriptor &descriptor{staticDescriptor.descriptor()}; 1064 descriptor.Establish( 1065 TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0); 1066 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1067 } 1068 1069 bool IONAME(InputComplex64)(Cookie cookie, double z[2]) { 1070 cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64"); 1071 StaticDescriptor staticDescriptor; 1072 Descriptor &descriptor{staticDescriptor.descriptor()}; 1073 descriptor.Establish( 1074 TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0); 1075 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1076 } 1077 1078 bool IONAME(OutputCharacter)( 1079 Cookie cookie, const char *x, std::size_t length, int kind) { 1080 cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter"); 1081 StaticDescriptor staticDescriptor; 1082 Descriptor &descriptor{staticDescriptor.descriptor()}; 1083 descriptor.Establish( 1084 kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0); 1085 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1086 } 1087 1088 bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { 1089 return IONAME(OutputCharacter(cookie, x, length, 1)); 1090 } 1091 1092 bool IONAME(InputCharacter)( 1093 Cookie cookie, char *x, std::size_t length, int kind) { 1094 cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter"); 1095 StaticDescriptor staticDescriptor; 1096 Descriptor &descriptor{staticDescriptor.descriptor()}; 1097 descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0); 1098 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1099 } 1100 1101 bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) { 1102 return IONAME(InputCharacter(cookie, x, length, 1)); 1103 } 1104 1105 bool IONAME(OutputLogical)(Cookie cookie, bool truth) { 1106 cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical"); 1107 StaticDescriptor staticDescriptor; 1108 Descriptor &descriptor{staticDescriptor.descriptor()}; 1109 descriptor.Establish( 1110 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0); 1111 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1112 } 1113 1114 bool IONAME(InputLogical)(Cookie cookie, bool &truth) { 1115 cookie->CheckFormattedStmtType<Direction::Input>("InputLogical"); 1116 StaticDescriptor staticDescriptor; 1117 Descriptor &descriptor{staticDescriptor.descriptor()}; 1118 descriptor.Establish( 1119 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0); 1120 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1121 } 1122 1123 std::size_t IONAME(GetSize)(Cookie cookie) { 1124 IoStatementState &io{*cookie}; 1125 if (const auto *formatted{ 1126 io.get_if<FormattedIoStatementState<Direction::Input>>()}) { 1127 return formatted->GetEditDescriptorChars(); 1128 } 1129 io.GetIoErrorHandler().Crash( 1130 "GetIoSize() called for an I/O statement that is not a formatted READ()"); 1131 return 0; 1132 } 1133 1134 std::size_t IONAME(GetIoLength)(Cookie cookie) { 1135 IoStatementState &io{*cookie}; 1136 if (const auto *inq{io.get_if<InquireIOLengthState>()}) { 1137 return inq->bytes(); 1138 } 1139 io.GetIoErrorHandler().Crash("GetIoLength() called for an I/O statement that " 1140 "is not INQUIRE(IOLENGTH=)"); 1141 return 0; 1142 } 1143 1144 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) { 1145 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 1146 if (handler.InError()) { // leave "msg" alone when no error 1147 handler.GetIoMsg(msg, length); 1148 } 1149 } 1150 1151 bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry, 1152 char *result, std::size_t length) { 1153 IoStatementState &io{*cookie}; 1154 return io.Inquire(inquiry, result, length); 1155 } 1156 1157 bool IONAME(InquireLogical)( 1158 Cookie cookie, InquiryKeywordHash inquiry, bool &result) { 1159 IoStatementState &io{*cookie}; 1160 return io.Inquire(inquiry, result); 1161 } 1162 1163 bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) { 1164 IoStatementState &io{*cookie}; 1165 return io.Inquire(HashInquiryKeyword("PENDING"), id, result); 1166 } 1167 1168 bool IONAME(InquireInteger64)( 1169 Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) { 1170 IoStatementState &io{*cookie}; 1171 std::int64_t n; 1172 if (io.Inquire(inquiry, n)) { 1173 SetInteger(result, kind, n); 1174 return true; 1175 } 1176 return false; 1177 } 1178 1179 enum Iostat IONAME(EndIoStatement)(Cookie cookie) { 1180 IoStatementState &io{*cookie}; 1181 return static_cast<enum Iostat>(io.EndIoStatement()); 1182 } 1183 } // namespace Fortran::runtime::io 1184