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