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 if (ChildIo * child{unit->GetChildIo()}) { 378 return &child->BeginIoStatement<ErroneousIoStatementState>( 379 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile, 380 sourceLine); 381 } else { 382 return &unit->BeginIoStatement<OpenStatementState>(terminator, *unit, 383 wasExtant, false /*not NEWUNIT=*/, sourceFile, sourceLine); 384 } 385 } else { 386 return NoopUnit(terminator, unitNumber, IostatBadUnitNumber); 387 } 388 } 389 390 Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) 391 const char *sourceFile, int sourceLine) { 392 Terminator terminator{sourceFile, sourceLine}; 393 ExternalFileUnit &unit{ 394 ExternalFileUnit::NewUnit(terminator, false /*not child I/O*/)}; 395 return &unit.BeginIoStatement<OpenStatementState>(terminator, unit, 396 false /*was an existing file*/, true /*NEWUNIT=*/, sourceFile, 397 sourceLine); 398 } 399 400 Cookie IONAME(BeginWait)(ExternalUnit unitNumber, AsynchronousId id, 401 const char *sourceFile, int sourceLine) { 402 Terminator terminator{sourceFile, sourceLine}; 403 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 404 if (unit->Wait(id)) { 405 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator, 406 *unit, ExternalMiscIoStatementState::Wait, sourceFile, sourceLine); 407 } else { 408 return &unit->BeginIoStatement<ErroneousIoStatementState>( 409 terminator, IostatBadWaitId, unit, sourceFile, sourceLine); 410 } 411 } else { 412 return NoopUnit( 413 terminator, unitNumber, id == 0 ? IostatOk : IostatBadWaitUnit); 414 } 415 } 416 Cookie IONAME(BeginWaitAll)( 417 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 418 return IONAME(BeginWait)(unitNumber, 0 /*no ID=*/, sourceFile, sourceLine); 419 } 420 421 Cookie IONAME(BeginClose)( 422 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 423 Terminator terminator{sourceFile, sourceLine}; 424 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 425 if (ChildIo * child{unit->GetChildIo()}) { 426 return &child->BeginIoStatement<ErroneousIoStatementState>( 427 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile, 428 sourceLine); 429 } 430 } 431 if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) { 432 return &unit->BeginIoStatement<CloseStatementState>( 433 terminator, *unit, sourceFile, sourceLine); 434 } else { 435 // CLOSE(UNIT=bad unit) is just a no-op 436 return NoopUnit(terminator, unitNumber); 437 } 438 } 439 440 Cookie IONAME(BeginFlush)( 441 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 442 Terminator terminator{sourceFile, sourceLine}; 443 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 444 if (ChildIo * child{unit->GetChildIo()}) { 445 return &child->BeginIoStatement<ExternalMiscIoStatementState>( 446 *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine); 447 } else { 448 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator, 449 *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine); 450 } 451 } else { 452 // FLUSH(UNIT=bad unit) is an error; an unconnected unit is a no-op 453 return NoopUnit(terminator, unitNumber, 454 unitNumber >= 0 ? IostatOk : IostatBadFlushUnit); 455 } 456 } 457 458 Cookie IONAME(BeginBackspace)( 459 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 460 Terminator terminator{sourceFile, sourceLine}; 461 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 462 if (ChildIo * child{unit->GetChildIo()}) { 463 return &child->BeginIoStatement<ErroneousIoStatementState>( 464 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile, 465 sourceLine); 466 } else { 467 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator, 468 *unit, ExternalMiscIoStatementState::Backspace, sourceFile, 469 sourceLine); 470 } 471 } else { 472 return NoopUnit(terminator, unitNumber, IostatBadBackspaceUnit); 473 } 474 } 475 476 Cookie IONAME(BeginEndfile)( 477 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 478 Terminator terminator{sourceFile, sourceLine}; 479 Cookie errorCookie{nullptr}; 480 if (ExternalFileUnit * 481 unit{GetOrCreateUnit(unitNumber, Direction::Output, std::nullopt, 482 terminator, errorCookie)}) { 483 if (ChildIo * child{unit->GetChildIo()}) { 484 return &child->BeginIoStatement<ErroneousIoStatementState>( 485 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile, 486 sourceLine); 487 } else { 488 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator, 489 *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine); 490 } 491 } else { 492 return errorCookie; 493 } 494 } 495 496 Cookie IONAME(BeginRewind)( 497 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 498 Terminator terminator{sourceFile, sourceLine}; 499 Cookie errorCookie{nullptr}; 500 if (ExternalFileUnit * 501 unit{GetOrCreateUnit(unitNumber, Direction::Input, std::nullopt, 502 terminator, errorCookie)}) { 503 if (ChildIo * child{unit->GetChildIo()}) { 504 return &child->BeginIoStatement<ErroneousIoStatementState>( 505 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile, 506 sourceLine); 507 } else { 508 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator, 509 *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine); 510 } 511 } else { 512 return errorCookie; 513 } 514 } 515 516 Cookie IONAME(BeginInquireUnit)( 517 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 518 Terminator terminator{sourceFile, sourceLine}; 519 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 520 if (ChildIo * child{unit->GetChildIo()}) { 521 return &child->BeginIoStatement<InquireUnitState>( 522 *unit, sourceFile, sourceLine); 523 } else { 524 return &unit->BeginIoStatement<InquireUnitState>( 525 terminator, *unit, sourceFile, sourceLine); 526 } 527 } else { 528 // INQUIRE(UNIT=unrecognized unit) 529 return &New<InquireNoUnitState>{terminator}( 530 sourceFile, sourceLine, unitNumber) 531 .release() 532 ->ioStatementState(); 533 } 534 } 535 536 Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength, 537 const char *sourceFile, int sourceLine) { 538 Terminator terminator{sourceFile, sourceLine}; 539 auto trimmed{SaveDefaultCharacter( 540 path, TrimTrailingSpaces(path, pathLength), terminator)}; 541 if (ExternalFileUnit * 542 unit{ExternalFileUnit::LookUp( 543 trimmed.get(), std::strlen(trimmed.get()))}) { 544 // INQUIRE(FILE=) to a connected unit 545 if (ChildIo * child{unit->GetChildIo()}) { 546 return &child->BeginIoStatement<InquireUnitState>( 547 *unit, sourceFile, sourceLine); 548 } else { 549 return &unit->BeginIoStatement<InquireUnitState>( 550 terminator, *unit, sourceFile, sourceLine); 551 } 552 } else { 553 return &New<InquireUnconnectedFileState>{terminator}( 554 std::move(trimmed), sourceFile, sourceLine) 555 .release() 556 ->ioStatementState(); 557 } 558 } 559 560 Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) { 561 Terminator oom{sourceFile, sourceLine}; 562 return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine) 563 .release() 564 ->ioStatementState(); 565 } 566 567 // Control list items 568 569 void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr, 570 bool hasEnd, bool hasEor, bool hasIoMsg) { 571 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 572 if (hasIoStat) { 573 handler.HasIoStat(); 574 } 575 if (hasErr) { 576 handler.HasErrLabel(); 577 } 578 if (hasEnd) { 579 handler.HasEndLabel(); 580 } 581 if (hasEor) { 582 handler.HasEorLabel(); 583 } 584 if (hasIoMsg) { 585 handler.HasIoMsg(); 586 } 587 } 588 589 static bool YesOrNo(const char *keyword, std::size_t length, const char *what, 590 IoErrorHandler &handler) { 591 static const char *keywords[]{"YES", "NO", nullptr}; 592 switch (IdentifyValue(keyword, length, keywords)) { 593 case 0: 594 return true; 595 case 1: 596 return false; 597 default: 598 handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what, 599 static_cast<int>(length), keyword); 600 return false; 601 } 602 } 603 604 bool IONAME(SetAdvance)( 605 Cookie cookie, const char *keyword, std::size_t length) { 606 IoStatementState &io{*cookie}; 607 IoErrorHandler &handler{io.GetIoErrorHandler()}; 608 bool nonAdvancing{!YesOrNo(keyword, length, "ADVANCE", handler)}; 609 if (nonAdvancing && io.GetConnectionState().access == Access::Direct) { 610 handler.SignalError("Non-advancing I/O attempted on direct access file"); 611 } else { 612 auto *unit{io.GetExternalFileUnit()}; 613 if (unit && unit->GetChildIo()) { 614 // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3) 615 } else { 616 io.mutableModes().nonAdvancing = nonAdvancing; 617 } 618 } 619 return !handler.InError(); 620 } 621 622 bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { 623 IoStatementState &io{*cookie}; 624 static const char *keywords[]{"NULL", "ZERO", nullptr}; 625 switch (IdentifyValue(keyword, length, keywords)) { 626 case 0: 627 io.mutableModes().editingFlags &= ~blankZero; 628 return true; 629 case 1: 630 io.mutableModes().editingFlags |= blankZero; 631 return true; 632 default: 633 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 634 "Invalid BLANK='%.*s'", static_cast<int>(length), keyword); 635 return false; 636 } 637 } 638 639 bool IONAME(SetDecimal)( 640 Cookie cookie, const char *keyword, std::size_t length) { 641 IoStatementState &io{*cookie}; 642 static const char *keywords[]{"COMMA", "POINT", nullptr}; 643 switch (IdentifyValue(keyword, length, keywords)) { 644 case 0: 645 io.mutableModes().editingFlags |= decimalComma; 646 return true; 647 case 1: 648 io.mutableModes().editingFlags &= ~decimalComma; 649 return true; 650 default: 651 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 652 "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword); 653 return false; 654 } 655 } 656 657 bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) { 658 IoStatementState &io{*cookie}; 659 static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr}; 660 switch (IdentifyValue(keyword, length, keywords)) { 661 case 0: 662 io.mutableModes().delim = '\''; 663 return true; 664 case 1: 665 io.mutableModes().delim = '"'; 666 return true; 667 case 2: 668 io.mutableModes().delim = '\0'; 669 return true; 670 default: 671 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 672 "Invalid DELIM='%.*s'", static_cast<int>(length), keyword); 673 return false; 674 } 675 } 676 677 bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { 678 IoStatementState &io{*cookie}; 679 IoErrorHandler &handler{io.GetIoErrorHandler()}; 680 io.mutableModes().pad = YesOrNo(keyword, length, "PAD", handler); 681 return !handler.InError(); 682 } 683 684 bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) { 685 IoStatementState &io{*cookie}; 686 IoErrorHandler &handler{io.GetIoErrorHandler()}; 687 if (auto *unit{io.GetExternalFileUnit()}) { 688 return unit->SetStreamPos(pos, handler); 689 } else if (!io.get_if<ErroneousIoStatementState>()) { 690 handler.Crash("SetPos() called on internal unit"); 691 } 692 return false; 693 } 694 695 bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) { 696 IoStatementState &io{*cookie}; 697 IoErrorHandler &handler{io.GetIoErrorHandler()}; 698 if (auto *unit{io.GetExternalFileUnit()}) { 699 if (unit->GetChildIo()) { 700 handler.SignalError( 701 IostatBadOpOnChildUnit, "REC= specifier on child I/O"); 702 } else { 703 unit->SetDirectRec(rec, handler); 704 } 705 } else if (!io.get_if<ErroneousIoStatementState>()) { 706 handler.Crash("SetRec() called on internal unit"); 707 } 708 return true; 709 } 710 711 bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { 712 IoStatementState &io{*cookie}; 713 static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE", 714 "PROCESSOR_DEFINED", nullptr}; 715 switch (IdentifyValue(keyword, length, keywords)) { 716 case 0: 717 io.mutableModes().round = decimal::RoundUp; 718 return true; 719 case 1: 720 io.mutableModes().round = decimal::RoundDown; 721 return true; 722 case 2: 723 io.mutableModes().round = decimal::RoundToZero; 724 return true; 725 case 3: 726 io.mutableModes().round = decimal::RoundNearest; 727 return true; 728 case 4: 729 io.mutableModes().round = decimal::RoundCompatible; 730 return true; 731 case 5: 732 io.mutableModes().round = executionEnvironment.defaultOutputRoundingMode; 733 return true; 734 default: 735 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 736 "Invalid ROUND='%.*s'", static_cast<int>(length), keyword); 737 return false; 738 } 739 } 740 741 bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) { 742 IoStatementState &io{*cookie}; 743 static const char *keywords[]{ 744 "PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr}; 745 switch (IdentifyValue(keyword, length, keywords)) { 746 case 0: 747 io.mutableModes().editingFlags |= signPlus; 748 return true; 749 case 1: 750 case 2: // processor default is SS 751 io.mutableModes().editingFlags &= ~signPlus; 752 return true; 753 default: 754 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 755 "Invalid SIGN='%.*s'", static_cast<int>(length), keyword); 756 return false; 757 } 758 } 759 760 bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) { 761 IoStatementState &io{*cookie}; 762 auto *open{io.get_if<OpenStatementState>()}; 763 if (!open) { 764 if (!io.get_if<ErroneousIoStatementState>()) { 765 io.GetIoErrorHandler().Crash( 766 "SetAccess() called when not in an OPEN statement"); 767 } 768 return false; 769 } else if (open->completedOperation()) { 770 io.GetIoErrorHandler().Crash( 771 "SetAccess() called after GetNewUnit() for an OPEN statement"); 772 } 773 static const char *keywords[]{ 774 "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr}; 775 switch (IdentifyValue(keyword, length, keywords)) { 776 case 0: 777 open->set_access(Access::Sequential); 778 break; 779 case 1: 780 open->set_access(Access::Direct); 781 break; 782 case 2: 783 open->set_access(Access::Stream); 784 break; 785 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND 786 open->set_position(Position::Append); 787 break; 788 default: 789 open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'", 790 static_cast<int>(length), keyword); 791 } 792 return true; 793 } 794 795 bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) { 796 IoStatementState &io{*cookie}; 797 auto *open{io.get_if<OpenStatementState>()}; 798 if (!open) { 799 if (!io.get_if<ErroneousIoStatementState>()) { 800 io.GetIoErrorHandler().Crash( 801 "SetAction() called when not in an OPEN statement"); 802 } 803 return false; 804 } else if (open->completedOperation()) { 805 io.GetIoErrorHandler().Crash( 806 "SetAction() called after GetNewUnit() for an OPEN statement"); 807 } 808 std::optional<Action> action; 809 static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr}; 810 switch (IdentifyValue(keyword, length, keywords)) { 811 case 0: 812 action = Action::Read; 813 break; 814 case 1: 815 action = Action::Write; 816 break; 817 case 2: 818 action = Action::ReadWrite; 819 break; 820 default: 821 open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'", 822 static_cast<int>(length), keyword); 823 return false; 824 } 825 RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value()); 826 if (open->wasExtant()) { 827 if ((*action != Action::Write) != open->unit().mayRead() || 828 (*action != Action::Read) != open->unit().mayWrite()) { 829 open->SignalError("ACTION= may not be changed on an open unit"); 830 } 831 } 832 open->set_action(*action); 833 return true; 834 } 835 836 bool IONAME(SetAsynchronous)( 837 Cookie cookie, const char *keyword, std::size_t length) { 838 IoStatementState &io{*cookie}; 839 IoErrorHandler &handler{io.GetIoErrorHandler()}; 840 bool isYes{YesOrNo(keyword, length, "ASYNCHRONOUS", handler)}; 841 if (auto *open{io.get_if<OpenStatementState>()}) { 842 if (open->completedOperation()) { 843 handler.Crash( 844 "SetAsynchronous() called after GetNewUnit() for an OPEN statement"); 845 } 846 open->unit().set_mayAsynchronous(isYes); 847 } else if (auto *ext{io.get_if<ExternalIoStatementBase>()}) { 848 if (isYes) { 849 if (ext->unit().mayAsynchronous()) { 850 ext->SetAsynchronous(); 851 } else { 852 handler.SignalError(IostatBadAsynchronous); 853 } 854 } 855 } else if (!io.get_if<ErroneousIoStatementState>()) { 856 handler.Crash("SetAsynchronous() called when not in an OPEN or external " 857 "I/O statement"); 858 } 859 return !handler.InError(); 860 } 861 862 bool IONAME(SetCarriagecontrol)( 863 Cookie cookie, const char *keyword, std::size_t length) { 864 IoStatementState &io{*cookie}; 865 auto *open{io.get_if<OpenStatementState>()}; 866 if (!open) { 867 if (!io.get_if<ErroneousIoStatementState>()) { 868 io.GetIoErrorHandler().Crash( 869 "SetCarriageControl() called when not in an OPEN statement"); 870 } 871 return false; 872 } else if (open->completedOperation()) { 873 io.GetIoErrorHandler().Crash( 874 "SetCarriageControl() called after GetNewUnit() for an OPEN statement"); 875 } 876 static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr}; 877 switch (IdentifyValue(keyword, length, keywords)) { 878 case 0: 879 return true; 880 case 1: 881 case 2: 882 open->SignalError(IostatErrorInKeyword, 883 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length), 884 keyword); 885 return false; 886 default: 887 open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'", 888 static_cast<int>(length), keyword); 889 return false; 890 } 891 } 892 893 bool IONAME(SetConvert)( 894 Cookie cookie, const char *keyword, std::size_t length) { 895 IoStatementState &io{*cookie}; 896 auto *open{io.get_if<OpenStatementState>()}; 897 if (!open) { 898 if (!io.get_if<ErroneousIoStatementState>()) { 899 io.GetIoErrorHandler().Crash( 900 "SetConvert() called when not in an OPEN statement"); 901 } 902 return false; 903 } else if (open->completedOperation()) { 904 io.GetIoErrorHandler().Crash( 905 "SetConvert() called after GetNewUnit() for an OPEN statement"); 906 } 907 if (auto convert{GetConvertFromString(keyword, length)}) { 908 open->set_convert(*convert); 909 return true; 910 } else { 911 open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'", 912 static_cast<int>(length), keyword); 913 return false; 914 } 915 } 916 917 bool IONAME(SetEncoding)( 918 Cookie cookie, const char *keyword, std::size_t length) { 919 IoStatementState &io{*cookie}; 920 auto *open{io.get_if<OpenStatementState>()}; 921 if (!open) { 922 if (!io.get_if<ErroneousIoStatementState>()) { 923 io.GetIoErrorHandler().Crash( 924 "SetEncoding() called when not in an OPEN statement"); 925 } 926 return false; 927 } else if (open->completedOperation()) { 928 io.GetIoErrorHandler().Crash( 929 "SetEncoding() called after GetNewUnit() for an OPEN statement"); 930 } 931 // Allow the encoding to be changed on an open unit -- it's 932 // useful and safe. 933 static const char *keywords[]{"UTF-8", "DEFAULT", nullptr}; 934 switch (IdentifyValue(keyword, length, keywords)) { 935 case 0: 936 open->unit().isUTF8 = true; 937 break; 938 case 1: 939 open->unit().isUTF8 = false; 940 break; 941 default: 942 open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'", 943 static_cast<int>(length), keyword); 944 } 945 return true; 946 } 947 948 bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) { 949 IoStatementState &io{*cookie}; 950 auto *open{io.get_if<OpenStatementState>()}; 951 if (!open) { 952 if (!io.get_if<ErroneousIoStatementState>()) { 953 io.GetIoErrorHandler().Crash( 954 "SetForm() called when not in an OPEN statement"); 955 } 956 } else if (open->completedOperation()) { 957 io.GetIoErrorHandler().Crash( 958 "SetForm() called after GetNewUnit() for an OPEN statement"); 959 } 960 static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr}; 961 switch (IdentifyValue(keyword, length, keywords)) { 962 case 0: 963 open->set_isUnformatted(false); 964 break; 965 case 1: 966 open->set_isUnformatted(true); 967 break; 968 default: 969 open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'", 970 static_cast<int>(length), keyword); 971 } 972 return true; 973 } 974 975 bool IONAME(SetPosition)( 976 Cookie cookie, const char *keyword, std::size_t length) { 977 IoStatementState &io{*cookie}; 978 auto *open{io.get_if<OpenStatementState>()}; 979 if (!open) { 980 if (!io.get_if<ErroneousIoStatementState>()) { 981 io.GetIoErrorHandler().Crash( 982 "SetPosition() called when not in an OPEN statement"); 983 } 984 return false; 985 } else if (open->completedOperation()) { 986 io.GetIoErrorHandler().Crash( 987 "SetPosition() called after GetNewUnit() for an OPEN statement"); 988 } 989 static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr}; 990 switch (IdentifyValue(keyword, length, positions)) { 991 case 0: 992 open->set_position(Position::AsIs); 993 return true; 994 case 1: 995 open->set_position(Position::Rewind); 996 return true; 997 case 2: 998 open->set_position(Position::Append); 999 return true; 1000 default: 1001 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 1002 "Invalid POSITION='%.*s'", static_cast<int>(length), keyword); 1003 } 1004 return true; 1005 } 1006 1007 bool IONAME(SetRecl)(Cookie cookie, std::size_t n) { 1008 IoStatementState &io{*cookie}; 1009 auto *open{io.get_if<OpenStatementState>()}; 1010 if (!open) { 1011 if (!io.get_if<ErroneousIoStatementState>()) { 1012 io.GetIoErrorHandler().Crash( 1013 "SetRecl() called when not in an OPEN statement"); 1014 } 1015 return false; 1016 } else if (open->completedOperation()) { 1017 io.GetIoErrorHandler().Crash( 1018 "SetRecl() called after GetNewUnit() for an OPEN statement"); 1019 } 1020 if (n <= 0) { 1021 io.GetIoErrorHandler().SignalError("RECL= must be greater than zero"); 1022 return false; 1023 } else if (open->wasExtant() && 1024 open->unit().openRecl.value_or(0) != static_cast<std::int64_t>(n)) { 1025 open->SignalError("RECL= may not be changed for an open unit"); 1026 return false; 1027 } else { 1028 open->unit().openRecl = n; 1029 return true; 1030 } 1031 } 1032 1033 bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { 1034 IoStatementState &io{*cookie}; 1035 if (auto *open{io.get_if<OpenStatementState>()}) { 1036 if (open->completedOperation()) { 1037 io.GetIoErrorHandler().Crash( 1038 "SetStatus() called after GetNewUnit() for an OPEN statement"); 1039 } 1040 static const char *statuses[]{ 1041 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; 1042 switch (IdentifyValue(keyword, length, statuses)) { 1043 case 0: 1044 open->set_status(OpenStatus::Old); 1045 return true; 1046 case 1: 1047 open->set_status(OpenStatus::New); 1048 return true; 1049 case 2: 1050 open->set_status(OpenStatus::Scratch); 1051 return true; 1052 case 3: 1053 open->set_status(OpenStatus::Replace); 1054 return true; 1055 case 4: 1056 open->set_status(OpenStatus::Unknown); 1057 return true; 1058 default: 1059 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 1060 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 1061 } 1062 return false; 1063 } 1064 if (auto *close{io.get_if<CloseStatementState>()}) { 1065 static const char *statuses[]{"KEEP", "DELETE", nullptr}; 1066 switch (IdentifyValue(keyword, length, statuses)) { 1067 case 0: 1068 close->set_status(CloseStatus::Keep); 1069 return true; 1070 case 1: 1071 close->set_status(CloseStatus::Delete); 1072 return true; 1073 default: 1074 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 1075 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 1076 } 1077 return false; 1078 } 1079 if (io.get_if<NoopStatementState>() || 1080 io.get_if<ErroneousIoStatementState>()) { 1081 return true; // don't bother validating STATUS= in a no-op CLOSE 1082 } 1083 io.GetIoErrorHandler().Crash( 1084 "SetStatus() called when not in an OPEN or CLOSE statement"); 1085 } 1086 1087 bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) { 1088 IoStatementState &io{*cookie}; 1089 if (auto *open{io.get_if<OpenStatementState>()}) { 1090 if (open->completedOperation()) { 1091 io.GetIoErrorHandler().Crash( 1092 "SetFile() called after GetNewUnit() for an OPEN statement"); 1093 } 1094 open->set_path(path, chars); 1095 return true; 1096 } else if (!io.get_if<ErroneousIoStatementState>()) { 1097 io.GetIoErrorHandler().Crash( 1098 "SetFile() called when not in an OPEN statement"); 1099 } 1100 return false; 1101 } 1102 1103 bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) { 1104 IoStatementState &io{*cookie}; 1105 auto *open{io.get_if<OpenStatementState>()}; 1106 if (!open) { 1107 if (!io.get_if<ErroneousIoStatementState>()) { 1108 io.GetIoErrorHandler().Crash( 1109 "GetNewUnit() called when not in an OPEN statement"); 1110 } 1111 return false; 1112 } else if (!open->InError()) { 1113 open->CompleteOperation(); 1114 } 1115 if (open->InError()) { 1116 // A failed OPEN(NEWUNIT=n) does not modify 'n' 1117 return false; 1118 } 1119 std::int64_t result{open->unit().unitNumber()}; 1120 if (!SetInteger(unit, kind, result)) { 1121 open->SignalError("GetNewUnit(): bad INTEGER kind(%d) or out-of-range " 1122 "value(%jd) for result", 1123 kind, static_cast<std::intmax_t>(result)); 1124 } 1125 return true; 1126 } 1127 1128 // Data transfers 1129 1130 bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 1131 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1132 } 1133 1134 bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 1135 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1136 } 1137 1138 bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x, 1139 std::size_t length, std::size_t elementBytes) { 1140 IoStatementState &io{*cookie}; 1141 if (auto *unf{io.get_if< 1142 ExternalUnformattedIoStatementState<Direction::Output>>()}) { 1143 return unf->Emit(x, length, elementBytes); 1144 } else if (auto *inq{io.get_if<InquireIOLengthState>()}) { 1145 return inq->Emit(x, length, elementBytes); 1146 } else if (!io.get_if<ErroneousIoStatementState>()) { 1147 io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O " 1148 "statement that is not unformatted output"); 1149 } 1150 return false; 1151 } 1152 1153 bool IONAME(InputUnformattedBlock)( 1154 Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) { 1155 IoStatementState &io{*cookie}; 1156 IoErrorHandler &handler{io.GetIoErrorHandler()}; 1157 io.BeginReadingRecord(); 1158 if (handler.InError()) { 1159 return false; 1160 } 1161 if (auto *unf{ 1162 io.get_if<ExternalUnformattedIoStatementState<Direction::Input>>()}) { 1163 return unf->Receive(x, length, elementBytes); 1164 } else if (!io.get_if<ErroneousIoStatementState>()) { 1165 handler.Crash("InputUnformattedBlock() called for an I/O statement that is " 1166 "not unformatted input"); 1167 } 1168 return false; 1169 } 1170 1171 bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) { 1172 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger8")) { 1173 return false; 1174 } 1175 StaticDescriptor staticDescriptor; 1176 Descriptor &descriptor{staticDescriptor.descriptor()}; 1177 descriptor.Establish( 1178 TypeCategory::Integer, 1, reinterpret_cast<void *>(&n), 0); 1179 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1180 } 1181 1182 bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) { 1183 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger16")) { 1184 return false; 1185 } 1186 StaticDescriptor staticDescriptor; 1187 Descriptor &descriptor{staticDescriptor.descriptor()}; 1188 descriptor.Establish( 1189 TypeCategory::Integer, 2, reinterpret_cast<void *>(&n), 0); 1190 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1191 } 1192 1193 bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) { 1194 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger32")) { 1195 return false; 1196 } 1197 StaticDescriptor staticDescriptor; 1198 Descriptor &descriptor{staticDescriptor.descriptor()}; 1199 descriptor.Establish( 1200 TypeCategory::Integer, 4, reinterpret_cast<void *>(&n), 0); 1201 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1202 } 1203 1204 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { 1205 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64")) { 1206 return false; 1207 } 1208 StaticDescriptor staticDescriptor; 1209 Descriptor &descriptor{staticDescriptor.descriptor()}; 1210 descriptor.Establish( 1211 TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0); 1212 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1213 } 1214 1215 #ifdef __SIZEOF_INT128__ 1216 bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) { 1217 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger128")) { 1218 return false; 1219 } 1220 StaticDescriptor staticDescriptor; 1221 Descriptor &descriptor{staticDescriptor.descriptor()}; 1222 descriptor.Establish( 1223 TypeCategory::Integer, 16, reinterpret_cast<void *>(&n), 0); 1224 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1225 } 1226 #endif 1227 1228 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { 1229 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputInteger")) { 1230 return false; 1231 } 1232 StaticDescriptor staticDescriptor; 1233 Descriptor &descriptor{staticDescriptor.descriptor()}; 1234 descriptor.Establish( 1235 TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0); 1236 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1237 } 1238 1239 bool IONAME(OutputReal32)(Cookie cookie, float x) { 1240 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32")) { 1241 return false; 1242 } 1243 StaticDescriptor staticDescriptor; 1244 Descriptor &descriptor{staticDescriptor.descriptor()}; 1245 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 1246 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1247 } 1248 1249 bool IONAME(OutputReal64)(Cookie cookie, double x) { 1250 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64")) { 1251 return false; 1252 } 1253 StaticDescriptor staticDescriptor; 1254 Descriptor &descriptor{staticDescriptor.descriptor()}; 1255 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 1256 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1257 } 1258 1259 bool IONAME(InputReal32)(Cookie cookie, float &x) { 1260 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputReal32")) { 1261 return false; 1262 } 1263 StaticDescriptor staticDescriptor; 1264 Descriptor &descriptor{staticDescriptor.descriptor()}; 1265 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 1266 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1267 } 1268 1269 bool IONAME(InputReal64)(Cookie cookie, double &x) { 1270 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputReal64")) { 1271 return false; 1272 } 1273 StaticDescriptor staticDescriptor; 1274 Descriptor &descriptor{staticDescriptor.descriptor()}; 1275 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 1276 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1277 } 1278 1279 bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) { 1280 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32")) { 1281 return false; 1282 } 1283 float z[2]{r, i}; 1284 StaticDescriptor staticDescriptor; 1285 Descriptor &descriptor{staticDescriptor.descriptor()}; 1286 descriptor.Establish( 1287 TypeCategory::Complex, 4, reinterpret_cast<void *>(&z), 0); 1288 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1289 } 1290 1291 bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) { 1292 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64")) { 1293 return false; 1294 } 1295 double z[2]{r, i}; 1296 StaticDescriptor staticDescriptor; 1297 Descriptor &descriptor{staticDescriptor.descriptor()}; 1298 descriptor.Establish( 1299 TypeCategory::Complex, 8, reinterpret_cast<void *>(&z), 0); 1300 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1301 } 1302 1303 bool IONAME(InputComplex32)(Cookie cookie, float z[2]) { 1304 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32")) { 1305 return false; 1306 } 1307 StaticDescriptor staticDescriptor; 1308 Descriptor &descriptor{staticDescriptor.descriptor()}; 1309 descriptor.Establish( 1310 TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0); 1311 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1312 } 1313 1314 bool IONAME(InputComplex64)(Cookie cookie, double z[2]) { 1315 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64")) { 1316 return false; 1317 } 1318 StaticDescriptor staticDescriptor; 1319 Descriptor &descriptor{staticDescriptor.descriptor()}; 1320 descriptor.Establish( 1321 TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0); 1322 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1323 } 1324 1325 bool IONAME(OutputCharacter)( 1326 Cookie cookie, const char *x, std::size_t length, int kind) { 1327 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter")) { 1328 return false; 1329 } 1330 StaticDescriptor staticDescriptor; 1331 Descriptor &descriptor{staticDescriptor.descriptor()}; 1332 descriptor.Establish( 1333 kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0); 1334 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1335 } 1336 1337 bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { 1338 return IONAME(OutputCharacter(cookie, x, length, 1)); 1339 } 1340 1341 bool IONAME(InputCharacter)( 1342 Cookie cookie, char *x, std::size_t length, int kind) { 1343 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter")) { 1344 return false; 1345 } 1346 StaticDescriptor staticDescriptor; 1347 Descriptor &descriptor{staticDescriptor.descriptor()}; 1348 descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0); 1349 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1350 } 1351 1352 bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) { 1353 return IONAME(InputCharacter)(cookie, x, length, 1); 1354 } 1355 1356 bool IONAME(OutputLogical)(Cookie cookie, bool truth) { 1357 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical")) { 1358 return false; 1359 } 1360 StaticDescriptor staticDescriptor; 1361 Descriptor &descriptor{staticDescriptor.descriptor()}; 1362 descriptor.Establish( 1363 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0); 1364 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1365 } 1366 1367 bool IONAME(InputLogical)(Cookie cookie, bool &truth) { 1368 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputLogical")) { 1369 return false; 1370 } 1371 StaticDescriptor staticDescriptor; 1372 Descriptor &descriptor{staticDescriptor.descriptor()}; 1373 descriptor.Establish( 1374 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0); 1375 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1376 } 1377 1378 bool IONAME(OutputDerivedType)(Cookie cookie, const Descriptor &descriptor, 1379 const NonTbpDefinedIoTable *table) { 1380 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor, table); 1381 } 1382 1383 bool IONAME(InputDerivedType)(Cookie cookie, const Descriptor &descriptor, 1384 const NonTbpDefinedIoTable *table) { 1385 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor, table); 1386 } 1387 1388 std::size_t IONAME(GetSize)(Cookie cookie) { 1389 IoStatementState &io{*cookie}; 1390 IoErrorHandler &handler{io.GetIoErrorHandler()}; 1391 if (!handler.InError()) { 1392 io.CompleteOperation(); 1393 } 1394 if (const auto *formatted{ 1395 io.get_if<FormattedIoStatementState<Direction::Input>>()}) { 1396 return formatted->GetEditDescriptorChars(); 1397 } else if (!io.get_if<ErroneousIoStatementState>()) { 1398 handler.Crash("GetIoSize() called for an I/O statement that is not a " 1399 "formatted READ()"); 1400 } 1401 return 0; 1402 } 1403 1404 std::size_t IONAME(GetIoLength)(Cookie cookie) { 1405 IoStatementState &io{*cookie}; 1406 IoErrorHandler &handler{io.GetIoErrorHandler()}; 1407 if (!handler.InError()) { 1408 io.CompleteOperation(); 1409 } 1410 if (const auto *inq{io.get_if<InquireIOLengthState>()}) { 1411 return inq->bytes(); 1412 } else if (!io.get_if<ErroneousIoStatementState>()) { 1413 handler.Crash("GetIoLength() called for an I/O statement that is not " 1414 "INQUIRE(IOLENGTH=)"); 1415 } 1416 return 0; 1417 } 1418 1419 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) { 1420 IoStatementState &io{*cookie}; 1421 IoErrorHandler &handler{io.GetIoErrorHandler()}; 1422 if (!handler.InError()) { 1423 io.CompleteOperation(); 1424 } 1425 if (handler.InError()) { // leave "msg" alone when no error 1426 handler.GetIoMsg(msg, length); 1427 } 1428 } 1429 1430 bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry, 1431 char *result, std::size_t length) { 1432 IoStatementState &io{*cookie}; 1433 return io.Inquire(inquiry, result, length); 1434 } 1435 1436 bool IONAME(InquireLogical)( 1437 Cookie cookie, InquiryKeywordHash inquiry, bool &result) { 1438 IoStatementState &io{*cookie}; 1439 return io.Inquire(inquiry, result); 1440 } 1441 1442 bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) { 1443 IoStatementState &io{*cookie}; 1444 return io.Inquire(HashInquiryKeyword("PENDING"), id, result); 1445 } 1446 1447 bool IONAME(InquireInteger64)( 1448 Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) { 1449 IoStatementState &io{*cookie}; 1450 std::int64_t n{0}; // safe "undefined" value 1451 if (io.Inquire(inquiry, n)) { 1452 if (SetInteger(result, kind, n)) { 1453 return true; 1454 } 1455 io.GetIoErrorHandler().SignalError( 1456 "InquireInteger64(): bad INTEGER kind(%d) or out-of-range " 1457 "value(%jd) for result", 1458 kind, static_cast<std::intmax_t>(n)); 1459 } 1460 return false; 1461 } 1462 1463 enum Iostat IONAME(EndIoStatement)(Cookie cookie) { 1464 IoStatementState &io{*cookie}; 1465 return static_cast<enum Iostat>(io.EndIoStatement()); 1466 } 1467 1468 template <typename INT> 1469 static enum Iostat CheckUnitNumberInRangeImpl(INT unit, bool handleError, 1470 char *ioMsg, std::size_t ioMsgLength, const char *sourceFile, 1471 int sourceLine) { 1472 static_assert(sizeof(INT) >= sizeof(ExternalUnit), 1473 "only intended to be used when the INT to ExternalUnit conversion is " 1474 "narrowing"); 1475 if (unit != static_cast<ExternalUnit>(unit)) { 1476 Terminator oom{sourceFile, sourceLine}; 1477 IoErrorHandler errorHandler{oom}; 1478 if (handleError) { 1479 errorHandler.HasIoStat(); 1480 if (ioMsg) { 1481 errorHandler.HasIoMsg(); 1482 } 1483 } 1484 // Only provide the bad unit number in the message if SignalError can print 1485 // it accurately. Otherwise, the generic IostatUnitOverflow message will be 1486 // used. 1487 if constexpr (sizeof(INT) > sizeof(std::intmax_t)) { 1488 errorHandler.SignalError(IostatUnitOverflow); 1489 } else if (static_cast<std::intmax_t>(unit) == unit) { 1490 errorHandler.SignalError(IostatUnitOverflow, 1491 "UNIT number %jd is out of range", static_cast<std::intmax_t>(unit)); 1492 } else { 1493 errorHandler.SignalError(IostatUnitOverflow); 1494 } 1495 if (ioMsg) { 1496 errorHandler.GetIoMsg(ioMsg, ioMsgLength); 1497 } 1498 return static_cast<enum Iostat>(errorHandler.GetIoStat()); 1499 } 1500 return IostatOk; 1501 } 1502 1503 enum Iostat IONAME(CheckUnitNumberInRange64)(std::int64_t unit, 1504 bool handleError, char *ioMsg, std::size_t ioMsgLength, 1505 const char *sourceFile, int sourceLine) { 1506 return CheckUnitNumberInRangeImpl( 1507 unit, handleError, ioMsg, ioMsgLength, sourceFile, sourceLine); 1508 } 1509 1510 #ifdef __SIZEOF_INT128__ 1511 enum Iostat IONAME(CheckUnitNumberInRange128)(common::int128_t unit, 1512 bool handleError, char *ioMsg, std::size_t ioMsgLength, 1513 const char *sourceFile, int sourceLine) { 1514 return CheckUnitNumberInRangeImpl( 1515 unit, handleError, ioMsg, ioMsgLength, sourceFile, sourceLine); 1516 } 1517 #endif 1518 1519 } // namespace Fortran::runtime::io 1520 1521 #if defined(_LIBCPP_VERBOSE_ABORT) 1522 // Provide own definition for `std::__libcpp_verbose_abort` to avoid dependency 1523 // on the version provided by libc++. 1524 1525 void std::__libcpp_verbose_abort(char const *format, ...) { 1526 va_list list; 1527 va_start(list, format); 1528 std::vfprintf(stderr, format, list); 1529 va_end(list); 1530 1531 std::abort(); 1532 } 1533 #endif 1534