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<NoopStatementState>{oom}(sourceFile, sourceLine) 321 .release() 322 ->ioStatementState(); 323 } 324 } 325 326 Cookie IONAME(BeginFlush)( 327 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 328 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 329 return &unit->BeginIoStatement<ExternalMiscIoStatementState>( 330 *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine); 331 } else { 332 // FLUSH(UNIT=unknown) is a no-op 333 Terminator oom{sourceFile, sourceLine}; 334 return &New<NoopStatementState>{oom}(sourceFile, sourceLine) 335 .release() 336 ->ioStatementState(); 337 } 338 } 339 340 Cookie IONAME(BeginBackspace)( 341 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 342 Terminator terminator{sourceFile, sourceLine}; 343 ExternalFileUnit &unit{ 344 ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; 345 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 346 unit, ExternalMiscIoStatementState::Backspace, sourceFile, sourceLine); 347 } 348 349 Cookie IONAME(BeginEndfile)( 350 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 351 Terminator terminator{sourceFile, sourceLine}; 352 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 353 unitNumber, Direction::Output, std::nullopt, terminator)}; 354 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 355 unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine); 356 } 357 358 Cookie IONAME(BeginRewind)( 359 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 360 Terminator terminator{sourceFile, sourceLine}; 361 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 362 unitNumber, Direction::Input, std::nullopt, terminator)}; 363 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 364 unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine); 365 } 366 367 Cookie IONAME(BeginInquireUnit)( 368 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 369 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 370 return &unit->BeginIoStatement<InquireUnitState>( 371 *unit, sourceFile, sourceLine); 372 } else { 373 // INQUIRE(UNIT=unrecognized unit) 374 Terminator oom{sourceFile, sourceLine}; 375 return &New<InquireNoUnitState>{oom}(sourceFile, sourceLine) 376 .release() 377 ->ioStatementState(); 378 } 379 } 380 381 Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength, 382 const char *sourceFile, int sourceLine) { 383 Terminator oom{sourceFile, sourceLine}; 384 auto trimmed{ 385 SaveDefaultCharacter(path, TrimTrailingSpaces(path, pathLength), oom)}; 386 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(trimmed.get())}) { 387 // INQUIRE(FILE=) to a connected unit 388 return &unit->BeginIoStatement<InquireUnitState>( 389 *unit, sourceFile, sourceLine); 390 } else { 391 return &New<InquireUnconnectedFileState>{oom}( 392 std::move(trimmed), sourceFile, sourceLine) 393 .release() 394 ->ioStatementState(); 395 } 396 } 397 398 Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) { 399 Terminator oom{sourceFile, sourceLine}; 400 return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine) 401 .release() 402 ->ioStatementState(); 403 } 404 405 // Control list items 406 407 void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr, 408 bool hasEnd, bool hasEor, bool hasIoMsg) { 409 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 410 if (hasIoStat) { 411 handler.HasIoStat(); 412 } 413 if (hasErr) { 414 handler.HasErrLabel(); 415 } 416 if (hasEnd) { 417 handler.HasEndLabel(); 418 } 419 if (hasEor) { 420 handler.HasEorLabel(); 421 } 422 if (hasIoMsg) { 423 handler.HasIoMsg(); 424 } 425 } 426 427 static bool YesOrNo(const char *keyword, std::size_t length, const char *what, 428 IoErrorHandler &handler) { 429 static const char *keywords[]{"YES", "NO", nullptr}; 430 switch (IdentifyValue(keyword, length, keywords)) { 431 case 0: 432 return true; 433 case 1: 434 return false; 435 default: 436 handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what, 437 static_cast<int>(length), keyword); 438 return false; 439 } 440 } 441 442 bool IONAME(SetAdvance)( 443 Cookie cookie, const char *keyword, std::size_t length) { 444 IoStatementState &io{*cookie}; 445 bool nonAdvancing{ 446 !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler())}; 447 if (nonAdvancing && io.GetConnectionState().access == Access::Direct) { 448 io.GetIoErrorHandler().SignalError( 449 "Non-advancing I/O attempted on direct access file"); 450 } else { 451 io.mutableModes().nonAdvancing = nonAdvancing; 452 } 453 return true; 454 } 455 456 bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { 457 IoStatementState &io{*cookie}; 458 ConnectionState &connection{io.GetConnectionState()}; 459 static const char *keywords[]{"NULL", "ZERO", nullptr}; 460 switch (IdentifyValue(keyword, length, keywords)) { 461 case 0: 462 connection.modes.editingFlags &= ~blankZero; 463 return true; 464 case 1: 465 connection.modes.editingFlags |= blankZero; 466 return true; 467 default: 468 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 469 "Invalid BLANK='%.*s'", static_cast<int>(length), keyword); 470 return false; 471 } 472 } 473 474 bool IONAME(SetDecimal)( 475 Cookie cookie, const char *keyword, std::size_t length) { 476 IoStatementState &io{*cookie}; 477 ConnectionState &connection{io.GetConnectionState()}; 478 static const char *keywords[]{"COMMA", "POINT", nullptr}; 479 switch (IdentifyValue(keyword, length, keywords)) { 480 case 0: 481 connection.modes.editingFlags |= decimalComma; 482 return true; 483 case 1: 484 connection.modes.editingFlags &= ~decimalComma; 485 return true; 486 default: 487 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 488 "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword); 489 return false; 490 } 491 } 492 493 bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) { 494 IoStatementState &io{*cookie}; 495 ConnectionState &connection{io.GetConnectionState()}; 496 static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr}; 497 switch (IdentifyValue(keyword, length, keywords)) { 498 case 0: 499 connection.modes.delim = '\''; 500 return true; 501 case 1: 502 connection.modes.delim = '"'; 503 return true; 504 case 2: 505 connection.modes.delim = '\0'; 506 return true; 507 default: 508 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 509 "Invalid DELIM='%.*s'", static_cast<int>(length), keyword); 510 return false; 511 } 512 } 513 514 bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { 515 IoStatementState &io{*cookie}; 516 ConnectionState &connection{io.GetConnectionState()}; 517 connection.modes.pad = 518 YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler()); 519 return true; 520 } 521 522 bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) { 523 IoStatementState &io{*cookie}; 524 ConnectionState &connection{io.GetConnectionState()}; 525 if (connection.access != Access::Stream) { 526 io.GetIoErrorHandler().SignalError( 527 "POS= may not appear unless ACCESS='STREAM'"); 528 return false; 529 } 530 if (pos < 1) { 531 io.GetIoErrorHandler().SignalError( 532 "POS=%zd is invalid", static_cast<std::intmax_t>(pos)); 533 return false; 534 } 535 if (auto *unit{io.GetExternalFileUnit()}) { 536 unit->SetPosition(pos); 537 return true; 538 } 539 io.GetIoErrorHandler().Crash("SetPos() on internal unit"); 540 return false; 541 } 542 543 bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) { 544 IoStatementState &io{*cookie}; 545 ConnectionState &connection{io.GetConnectionState()}; 546 if (connection.access != Access::Direct) { 547 io.GetIoErrorHandler().SignalError( 548 "REC= may not appear unless ACCESS='DIRECT'"); 549 return false; 550 } 551 if (!connection.openRecl) { 552 io.GetIoErrorHandler().SignalError("RECL= was not specified"); 553 return false; 554 } 555 if (rec < 1) { 556 io.GetIoErrorHandler().SignalError( 557 "REC=%zd is invalid", static_cast<std::intmax_t>(rec)); 558 return false; 559 } 560 connection.currentRecordNumber = rec; 561 if (auto *unit{io.GetExternalFileUnit()}) { 562 unit->SetPosition((rec - 1) * *connection.openRecl); 563 } 564 return true; 565 } 566 567 bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { 568 IoStatementState &io{*cookie}; 569 ConnectionState &connection{io.GetConnectionState()}; 570 static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE", 571 "PROCESSOR_DEFINED", nullptr}; 572 switch (IdentifyValue(keyword, length, keywords)) { 573 case 0: 574 connection.modes.round = decimal::RoundUp; 575 return true; 576 case 1: 577 connection.modes.round = decimal::RoundDown; 578 return true; 579 case 2: 580 connection.modes.round = decimal::RoundToZero; 581 return true; 582 case 3: 583 connection.modes.round = decimal::RoundNearest; 584 return true; 585 case 4: 586 connection.modes.round = decimal::RoundCompatible; 587 return true; 588 case 5: 589 connection.modes.round = executionEnvironment.defaultOutputRoundingMode; 590 return true; 591 default: 592 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 593 "Invalid ROUND='%.*s'", static_cast<int>(length), keyword); 594 return false; 595 } 596 } 597 598 bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) { 599 IoStatementState &io{*cookie}; 600 ConnectionState &connection{io.GetConnectionState()}; 601 static const char *keywords[]{ 602 "PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr}; 603 switch (IdentifyValue(keyword, length, keywords)) { 604 case 0: 605 connection.modes.editingFlags |= signPlus; 606 return true; 607 case 1: 608 case 2: // processor default is SS 609 connection.modes.editingFlags &= ~signPlus; 610 return true; 611 default: 612 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 613 "Invalid SIGN='%.*s'", static_cast<int>(length), keyword); 614 return false; 615 } 616 } 617 618 bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) { 619 IoStatementState &io{*cookie}; 620 auto *open{io.get_if<OpenStatementState>()}; 621 if (!open) { 622 io.GetIoErrorHandler().Crash( 623 "SetAccess() called when not in an OPEN statement"); 624 } 625 static const char *keywords[]{ 626 "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr}; 627 switch (IdentifyValue(keyword, length, keywords)) { 628 case 0: 629 open->set_access(Access::Sequential); 630 break; 631 case 1: 632 open->set_access(Access::Direct); 633 break; 634 case 2: 635 open->set_access(Access::Stream); 636 break; 637 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND 638 open->set_position(Position::Append); 639 break; 640 default: 641 open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'", 642 static_cast<int>(length), keyword); 643 } 644 return true; 645 } 646 647 bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) { 648 IoStatementState &io{*cookie}; 649 auto *open{io.get_if<OpenStatementState>()}; 650 if (!open) { 651 io.GetIoErrorHandler().Crash( 652 "SetAction() called when not in an OPEN statement"); 653 } 654 std::optional<Action> action; 655 static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr}; 656 switch (IdentifyValue(keyword, length, keywords)) { 657 case 0: 658 action = Action::Read; 659 break; 660 case 1: 661 action = Action::Write; 662 break; 663 case 2: 664 action = Action::ReadWrite; 665 break; 666 default: 667 open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'", 668 static_cast<int>(length), keyword); 669 return false; 670 } 671 RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value()); 672 if (open->wasExtant()) { 673 if ((*action != Action::Write) != open->unit().mayRead() || 674 (*action != Action::Read) != open->unit().mayWrite()) { 675 open->SignalError("ACTION= may not be changed on an open unit"); 676 } 677 } 678 open->set_action(*action); 679 return true; 680 } 681 682 bool IONAME(SetAsynchronous)( 683 Cookie cookie, const char *keyword, std::size_t length) { 684 IoStatementState &io{*cookie}; 685 auto *open{io.get_if<OpenStatementState>()}; 686 if (!open) { 687 io.GetIoErrorHandler().Crash( 688 "SetAsynchronous() called when not in an OPEN statement"); 689 } 690 static const char *keywords[]{"YES", "NO", nullptr}; 691 switch (IdentifyValue(keyword, length, keywords)) { 692 case 0: 693 open->unit().set_mayAsynchronous(true); 694 return true; 695 case 1: 696 open->unit().set_mayAsynchronous(false); 697 return true; 698 default: 699 open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'", 700 static_cast<int>(length), keyword); 701 return false; 702 } 703 } 704 705 bool IONAME(SetCarriagecontrol)( 706 Cookie cookie, const char *keyword, std::size_t length) { 707 IoStatementState &io{*cookie}; 708 auto *open{io.get_if<OpenStatementState>()}; 709 if (!open) { 710 io.GetIoErrorHandler().Crash( 711 "SetCarriageControl() called when not in an OPEN statement"); 712 } 713 static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr}; 714 switch (IdentifyValue(keyword, length, keywords)) { 715 case 0: 716 return true; 717 case 1: 718 case 2: 719 open->SignalError(IostatErrorInKeyword, 720 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length), 721 keyword); 722 return false; 723 default: 724 open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'", 725 static_cast<int>(length), keyword); 726 return false; 727 } 728 } 729 730 bool IONAME(SetConvert)( 731 Cookie cookie, const char *keyword, std::size_t length) { 732 IoStatementState &io{*cookie}; 733 auto *open{io.get_if<OpenStatementState>()}; 734 if (!open) { 735 io.GetIoErrorHandler().Crash( 736 "SetConvert() called when not in an OPEN statement"); 737 } 738 if (auto convert{GetConvertFromString(keyword, length)}) { 739 open->set_convert(*convert); 740 return true; 741 } else { 742 open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'", 743 static_cast<int>(length), keyword); 744 return false; 745 } 746 } 747 748 bool IONAME(SetEncoding)( 749 Cookie cookie, const char *keyword, std::size_t length) { 750 IoStatementState &io{*cookie}; 751 auto *open{io.get_if<OpenStatementState>()}; 752 if (!open) { 753 io.GetIoErrorHandler().Crash( 754 "SetEncoding() called when not in an OPEN statement"); 755 } 756 bool isUTF8{false}; 757 static const char *keywords[]{"UTF-8", "DEFAULT", nullptr}; 758 switch (IdentifyValue(keyword, length, keywords)) { 759 case 0: 760 isUTF8 = true; 761 break; 762 case 1: 763 isUTF8 = false; 764 break; 765 default: 766 open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'", 767 static_cast<int>(length), keyword); 768 } 769 if (isUTF8 != open->unit().isUTF8) { 770 if (open->wasExtant()) { 771 open->SignalError("ENCODING= may not be changed on an open unit"); 772 } 773 open->unit().isUTF8 = isUTF8; 774 } 775 return true; 776 } 777 778 bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) { 779 IoStatementState &io{*cookie}; 780 auto *open{io.get_if<OpenStatementState>()}; 781 if (!open) { 782 io.GetIoErrorHandler().Crash( 783 "SetForm() called when not in an OPEN statement"); 784 } 785 static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr}; 786 switch (IdentifyValue(keyword, length, keywords)) { 787 case 0: 788 open->set_isUnformatted(false); 789 break; 790 case 1: 791 open->set_isUnformatted(true); 792 break; 793 default: 794 open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'", 795 static_cast<int>(length), keyword); 796 } 797 return true; 798 } 799 800 bool IONAME(SetPosition)( 801 Cookie cookie, const char *keyword, std::size_t length) { 802 IoStatementState &io{*cookie}; 803 auto *open{io.get_if<OpenStatementState>()}; 804 if (!open) { 805 io.GetIoErrorHandler().Crash( 806 "SetPosition() called when not in an OPEN statement"); 807 } 808 static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr}; 809 switch (IdentifyValue(keyword, length, positions)) { 810 case 0: 811 open->set_position(Position::AsIs); 812 return true; 813 case 1: 814 open->set_position(Position::Rewind); 815 return true; 816 case 2: 817 open->set_position(Position::Append); 818 return true; 819 default: 820 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 821 "Invalid POSITION='%.*s'", static_cast<int>(length), keyword); 822 } 823 return true; 824 } 825 826 bool IONAME(SetRecl)(Cookie cookie, std::size_t n) { 827 IoStatementState &io{*cookie}; 828 auto *open{io.get_if<OpenStatementState>()}; 829 if (!open) { 830 io.GetIoErrorHandler().Crash( 831 "SetRecl() called when not in an OPEN statement"); 832 } 833 if (n <= 0) { 834 io.GetIoErrorHandler().SignalError("RECL= must be greater than zero"); 835 return false; 836 } else if (open->wasExtant() && 837 open->unit().openRecl.value_or(0) != static_cast<std::int64_t>(n)) { 838 open->SignalError("RECL= may not be changed for an open unit"); 839 return false; 840 } else { 841 open->unit().openRecl = n; 842 return true; 843 } 844 } 845 846 bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { 847 IoStatementState &io{*cookie}; 848 if (auto *open{io.get_if<OpenStatementState>()}) { 849 static const char *statuses[]{ 850 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; 851 switch (IdentifyValue(keyword, length, statuses)) { 852 case 0: 853 open->set_status(OpenStatus::Old); 854 return true; 855 case 1: 856 open->set_status(OpenStatus::New); 857 return true; 858 case 2: 859 open->set_status(OpenStatus::Scratch); 860 return true; 861 case 3: 862 open->set_status(OpenStatus::Replace); 863 return true; 864 case 4: 865 open->set_status(OpenStatus::Unknown); 866 return true; 867 default: 868 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 869 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 870 } 871 return false; 872 } 873 if (auto *close{io.get_if<CloseStatementState>()}) { 874 static const char *statuses[]{"KEEP", "DELETE", nullptr}; 875 switch (IdentifyValue(keyword, length, statuses)) { 876 case 0: 877 close->set_status(CloseStatus::Keep); 878 return true; 879 case 1: 880 close->set_status(CloseStatus::Delete); 881 return true; 882 default: 883 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 884 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 885 } 886 return false; 887 } 888 if (io.get_if<NoopStatementState>()) { 889 return true; // don't bother validating STATUS= in a no-op CLOSE 890 } 891 io.GetIoErrorHandler().Crash( 892 "SetStatus() called when not in an OPEN or CLOSE statement"); 893 } 894 895 bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) { 896 IoStatementState &io{*cookie}; 897 if (auto *open{io.get_if<OpenStatementState>()}) { 898 open->set_path(path, chars); 899 return true; 900 } 901 io.GetIoErrorHandler().Crash( 902 "SetFile() called when not in an OPEN statement"); 903 return false; 904 } 905 906 bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) { 907 IoStatementState &io{*cookie}; 908 auto *open{io.get_if<OpenStatementState>()}; 909 if (!open) { 910 io.GetIoErrorHandler().Crash( 911 "GetNewUnit() called when not in an OPEN statement"); 912 } 913 if (!SetInteger(unit, kind, open->unit().unitNumber())) { 914 open->SignalError("GetNewUnit(): Bad INTEGER kind(%d) for result"); 915 } 916 return true; 917 } 918 919 // Data transfers 920 921 bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 922 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 923 } 924 925 bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 926 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 927 } 928 929 bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x, 930 std::size_t length, std::size_t elementBytes) { 931 IoStatementState &io{*cookie}; 932 if (auto *unf{io.get_if< 933 ExternalUnformattedIoStatementState<Direction::Output>>()}) { 934 return unf->Emit(x, length, elementBytes); 935 } else if (auto *inq{io.get_if<InquireIOLengthState>()}) { 936 return inq->Emit(x, length, elementBytes); 937 } 938 io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O " 939 "statement that is not unformatted output"); 940 return false; 941 } 942 943 bool IONAME(InputUnformattedBlock)( 944 Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) { 945 IoStatementState &io{*cookie}; 946 io.BeginReadingRecord(); 947 if (io.GetIoErrorHandler().InError()) { 948 return false; 949 } 950 if (auto *unf{ 951 io.get_if<ExternalUnformattedIoStatementState<Direction::Input>>()}) { 952 return unf->Receive(x, length, elementBytes); 953 } 954 io.GetIoErrorHandler().Crash("InputUnformattedBlock() called for an I/O " 955 "statement that is not unformatted output"); 956 return false; 957 } 958 959 bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) { 960 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger8"); 961 StaticDescriptor staticDescriptor; 962 Descriptor &descriptor{staticDescriptor.descriptor()}; 963 descriptor.Establish( 964 TypeCategory::Integer, 1, reinterpret_cast<void *>(&n), 0); 965 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 966 } 967 968 bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) { 969 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger16"); 970 StaticDescriptor staticDescriptor; 971 Descriptor &descriptor{staticDescriptor.descriptor()}; 972 descriptor.Establish( 973 TypeCategory::Integer, 2, reinterpret_cast<void *>(&n), 0); 974 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 975 } 976 977 bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) { 978 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger32"); 979 StaticDescriptor staticDescriptor; 980 Descriptor &descriptor{staticDescriptor.descriptor()}; 981 descriptor.Establish( 982 TypeCategory::Integer, 4, reinterpret_cast<void *>(&n), 0); 983 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 984 } 985 986 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { 987 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64"); 988 StaticDescriptor staticDescriptor; 989 Descriptor &descriptor{staticDescriptor.descriptor()}; 990 descriptor.Establish( 991 TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0); 992 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 993 } 994 995 #ifdef __SIZEOF_INT128__ 996 bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) { 997 cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger128"); 998 StaticDescriptor staticDescriptor; 999 Descriptor &descriptor{staticDescriptor.descriptor()}; 1000 descriptor.Establish( 1001 TypeCategory::Integer, 16, reinterpret_cast<void *>(&n), 0); 1002 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1003 } 1004 #endif 1005 1006 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { 1007 cookie->CheckFormattedStmtType<Direction::Input>("InputInteger"); 1008 StaticDescriptor staticDescriptor; 1009 Descriptor &descriptor{staticDescriptor.descriptor()}; 1010 descriptor.Establish( 1011 TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0); 1012 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1013 } 1014 1015 bool IONAME(OutputReal32)(Cookie cookie, float x) { 1016 cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32"); 1017 StaticDescriptor staticDescriptor; 1018 Descriptor &descriptor{staticDescriptor.descriptor()}; 1019 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 1020 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1021 } 1022 1023 bool IONAME(OutputReal64)(Cookie cookie, double x) { 1024 cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64"); 1025 StaticDescriptor staticDescriptor; 1026 Descriptor &descriptor{staticDescriptor.descriptor()}; 1027 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 1028 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1029 } 1030 1031 bool IONAME(InputReal32)(Cookie cookie, float &x) { 1032 cookie->CheckFormattedStmtType<Direction::Input>("InputReal32"); 1033 StaticDescriptor staticDescriptor; 1034 Descriptor &descriptor{staticDescriptor.descriptor()}; 1035 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 1036 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1037 } 1038 1039 bool IONAME(InputReal64)(Cookie cookie, double &x) { 1040 cookie->CheckFormattedStmtType<Direction::Input>("InputReal64"); 1041 StaticDescriptor staticDescriptor; 1042 Descriptor &descriptor{staticDescriptor.descriptor()}; 1043 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 1044 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1045 } 1046 1047 bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) { 1048 cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32"); 1049 float z[2]{r, i}; 1050 StaticDescriptor staticDescriptor; 1051 Descriptor &descriptor{staticDescriptor.descriptor()}; 1052 descriptor.Establish( 1053 TypeCategory::Complex, 4, reinterpret_cast<void *>(&z), 0); 1054 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1055 } 1056 1057 bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) { 1058 cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64"); 1059 double z[2]{r, i}; 1060 StaticDescriptor staticDescriptor; 1061 Descriptor &descriptor{staticDescriptor.descriptor()}; 1062 descriptor.Establish( 1063 TypeCategory::Complex, 8, reinterpret_cast<void *>(&z), 0); 1064 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1065 } 1066 1067 bool IONAME(InputComplex32)(Cookie cookie, float z[2]) { 1068 cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32"); 1069 StaticDescriptor staticDescriptor; 1070 Descriptor &descriptor{staticDescriptor.descriptor()}; 1071 descriptor.Establish( 1072 TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0); 1073 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1074 } 1075 1076 bool IONAME(InputComplex64)(Cookie cookie, double z[2]) { 1077 cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64"); 1078 StaticDescriptor staticDescriptor; 1079 Descriptor &descriptor{staticDescriptor.descriptor()}; 1080 descriptor.Establish( 1081 TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0); 1082 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1083 } 1084 1085 bool IONAME(OutputCharacter)( 1086 Cookie cookie, const char *x, std::size_t length, int kind) { 1087 cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter"); 1088 StaticDescriptor staticDescriptor; 1089 Descriptor &descriptor{staticDescriptor.descriptor()}; 1090 descriptor.Establish( 1091 kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0); 1092 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1093 } 1094 1095 bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { 1096 return IONAME(OutputCharacter(cookie, x, length, 1)); 1097 } 1098 1099 bool IONAME(InputCharacter)( 1100 Cookie cookie, char *x, std::size_t length, int kind) { 1101 cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter"); 1102 StaticDescriptor staticDescriptor; 1103 Descriptor &descriptor{staticDescriptor.descriptor()}; 1104 descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0); 1105 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1106 } 1107 1108 bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) { 1109 return IONAME(InputCharacter(cookie, x, length, 1)); 1110 } 1111 1112 bool IONAME(OutputLogical)(Cookie cookie, bool truth) { 1113 cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical"); 1114 StaticDescriptor staticDescriptor; 1115 Descriptor &descriptor{staticDescriptor.descriptor()}; 1116 descriptor.Establish( 1117 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0); 1118 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1119 } 1120 1121 bool IONAME(InputLogical)(Cookie cookie, bool &truth) { 1122 cookie->CheckFormattedStmtType<Direction::Input>("InputLogical"); 1123 StaticDescriptor staticDescriptor; 1124 Descriptor &descriptor{staticDescriptor.descriptor()}; 1125 descriptor.Establish( 1126 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0); 1127 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1128 } 1129 1130 std::size_t IONAME(GetSize)(Cookie cookie) { 1131 IoStatementState &io{*cookie}; 1132 if (const auto *formatted{ 1133 io.get_if<FormattedIoStatementState<Direction::Input>>()}) { 1134 return formatted->GetEditDescriptorChars(); 1135 } 1136 io.GetIoErrorHandler().Crash( 1137 "GetIoSize() called for an I/O statement that is not a formatted READ()"); 1138 return 0; 1139 } 1140 1141 std::size_t IONAME(GetIoLength)(Cookie cookie) { 1142 IoStatementState &io{*cookie}; 1143 if (const auto *inq{io.get_if<InquireIOLengthState>()}) { 1144 return inq->bytes(); 1145 } 1146 io.GetIoErrorHandler().Crash("GetIoLength() called for an I/O statement that " 1147 "is not INQUIRE(IOLENGTH=)"); 1148 return 0; 1149 } 1150 1151 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) { 1152 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 1153 if (handler.InError()) { // leave "msg" alone when no error 1154 handler.GetIoMsg(msg, length); 1155 } 1156 } 1157 1158 bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry, 1159 char *result, std::size_t length) { 1160 IoStatementState &io{*cookie}; 1161 return io.Inquire(inquiry, result, length); 1162 } 1163 1164 bool IONAME(InquireLogical)( 1165 Cookie cookie, InquiryKeywordHash inquiry, bool &result) { 1166 IoStatementState &io{*cookie}; 1167 return io.Inquire(inquiry, result); 1168 } 1169 1170 bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) { 1171 IoStatementState &io{*cookie}; 1172 return io.Inquire(HashInquiryKeyword("PENDING"), id, result); 1173 } 1174 1175 bool IONAME(InquireInteger64)( 1176 Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) { 1177 IoStatementState &io{*cookie}; 1178 std::int64_t n; 1179 if (io.Inquire(inquiry, n)) { 1180 SetInteger(result, kind, n); 1181 return true; 1182 } 1183 return false; 1184 } 1185 1186 enum Iostat IONAME(EndIoStatement)(Cookie cookie) { 1187 IoStatementState &io{*cookie}; 1188 return static_cast<enum Iostat>(io.EndIoStatement()); 1189 } 1190 } // namespace Fortran::runtime::io 1191