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