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