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