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