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