1 //===-- runtime/io-api.cpp --------------------------------------*- C++ -*-===// 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 "io-api.h" 12 #include "descriptor-io.h" 13 #include "descriptor.h" 14 #include "edit-input.h" 15 #include "edit-output.h" 16 #include "environment.h" 17 #include "format.h" 18 #include "io-stmt.h" 19 #include "memory.h" 20 #include "terminator.h" 21 #include "tools.h" 22 #include "unit.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> 151 Cookie BeginExternalListIO( 152 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 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 /*formatted*/, terminator)}; 159 if (unit.access == Access::Direct) { 160 terminator.Crash("List-directed I/O attempted on direct access file"); 161 return nullptr; 162 } 163 if (unit.isUnformatted) { 164 terminator.Crash("List-directed I/O attempted on unformatted file"); 165 return nullptr; 166 } 167 IoErrorHandler handler{terminator}; 168 unit.SetDirection(DIR, handler); 169 IoStatementState &io{unit.BeginIoStatement<ExternalListIoStatementState<DIR>>( 170 unit, sourceFile, sourceLine)}; 171 return &io; 172 } 173 174 Cookie IONAME(BeginExternalListOutput)( 175 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 176 return BeginExternalListIO<Direction::Output>( 177 unitNumber, sourceFile, sourceLine); 178 } 179 180 Cookie IONAME(BeginExternalListInput)( 181 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 182 return BeginExternalListIO<Direction::Input>( 183 unitNumber, sourceFile, sourceLine); 184 } 185 186 template <Direction DIR> 187 Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength, 188 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 189 Terminator terminator{sourceFile, sourceLine}; 190 if (unitNumber == DefaultUnit) { 191 unitNumber = DIR == Direction::Input ? 5 : 6; 192 } 193 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 194 unitNumber, DIR, false /*formatted*/, terminator)}; 195 if (unit.isUnformatted) { 196 terminator.Crash("Formatted I/O attempted on unformatted file"); 197 return nullptr; 198 } 199 IoErrorHandler handler{terminator}; 200 unit.SetDirection(DIR, handler); 201 IoStatementState &io{ 202 unit.BeginIoStatement<ExternalFormattedIoStatementState<DIR>>( 203 unit, format, formatLength, sourceFile, sourceLine)}; 204 return &io; 205 } 206 207 Cookie IONAME(BeginExternalFormattedOutput)(const char *format, 208 std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, 209 int sourceLine) { 210 return BeginExternalFormattedIO<Direction::Output>( 211 format, formatLength, unitNumber, sourceFile, sourceLine); 212 } 213 214 Cookie IONAME(BeginExternalFormattedInput)(const char *format, 215 std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile, 216 int sourceLine) { 217 return BeginExternalFormattedIO<Direction::Input>( 218 format, formatLength, unitNumber, sourceFile, sourceLine); 219 } 220 221 template <Direction DIR> 222 Cookie BeginUnformattedIO( 223 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 224 Terminator terminator{sourceFile, sourceLine}; 225 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 226 unitNumber, DIR, true /*unformatted*/, terminator)}; 227 if (!unit.isUnformatted) { 228 terminator.Crash("Unformatted output attempted on formatted file"); 229 } 230 IoStatementState &io{unit.BeginIoStatement<UnformattedIoStatementState<DIR>>( 231 unit, sourceFile, sourceLine)}; 232 IoErrorHandler handler{terminator}; 233 unit.SetDirection(DIR, handler); 234 if constexpr (DIR == Direction::Output) { 235 if (unit.access == Access::Sequential && !unit.isFixedRecordLength) { 236 // Create space for (sub)record header to be completed by 237 // UnformattedIoStatementState<Direction::Output>::EndIoStatement() 238 io.Emit("\0\0\0\0", 4); // placeholder for record length header 239 } 240 } 241 return &io; 242 } 243 244 Cookie IONAME(BeginUnformattedOutput)( 245 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 246 return BeginUnformattedIO<Direction::Output>( 247 unitNumber, sourceFile, sourceLine); 248 } 249 250 Cookie IONAME(BeginUnformattedInput)( 251 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 252 return BeginUnformattedIO<Direction::Input>( 253 unitNumber, sourceFile, sourceLine); 254 } 255 256 Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=) 257 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 258 bool wasExtant{false}; 259 Terminator terminator{sourceFile, sourceLine}; 260 ExternalFileUnit &unit{ 261 ExternalFileUnit::LookUpOrCreate(unitNumber, terminator, wasExtant)}; 262 return &unit.BeginIoStatement<OpenStatementState>( 263 unit, wasExtant, sourceFile, sourceLine); 264 } 265 266 Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) 267 const char *sourceFile, int sourceLine) { 268 Terminator terminator{sourceFile, sourceLine}; 269 bool ignored{false}; 270 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreate( 271 ExternalFileUnit::NewUnit(terminator), terminator, ignored)}; 272 return &unit.BeginIoStatement<OpenStatementState>( 273 unit, false /*was an existing file*/, sourceFile, sourceLine); 274 } 275 276 Cookie IONAME(BeginClose)( 277 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 278 if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) { 279 return &unit->BeginIoStatement<CloseStatementState>( 280 *unit, sourceFile, sourceLine); 281 } else { 282 // CLOSE(UNIT=bad unit) is just a no-op 283 Terminator oom{sourceFile, sourceLine}; 284 return &New<NoopCloseStatementState>{oom}(sourceFile, sourceLine) 285 .release() 286 ->ioStatementState(); 287 } 288 } 289 290 Cookie IONAME(BeginFlush)( 291 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 292 Terminator terminator{sourceFile, sourceLine}; 293 ExternalFileUnit &unit{ 294 ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; 295 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 296 unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine); 297 } 298 299 Cookie IONAME(BeginBackspace)( 300 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 301 Terminator terminator{sourceFile, sourceLine}; 302 ExternalFileUnit &unit{ 303 ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)}; 304 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 305 unit, ExternalMiscIoStatementState::Backspace, sourceFile, sourceLine); 306 } 307 308 Cookie IONAME(BeginEndfile)( 309 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 310 Terminator terminator{sourceFile, sourceLine}; 311 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 312 unitNumber, Direction::Output, true /*formatted*/, terminator)}; 313 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 314 unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine); 315 } 316 317 Cookie IONAME(BeginRewind)( 318 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 319 Terminator terminator{sourceFile, sourceLine}; 320 ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( 321 unitNumber, Direction::Input, true /*formatted*/, terminator)}; 322 return &unit.BeginIoStatement<ExternalMiscIoStatementState>( 323 unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine); 324 } 325 326 Cookie IONAME(BeginInquireUnit)( 327 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { 328 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) { 329 return &unit->BeginIoStatement<InquireUnitState>( 330 *unit, sourceFile, sourceLine); 331 } else { 332 // INQUIRE(UNIT=unrecognized unit) 333 Terminator oom{sourceFile, sourceLine}; 334 return &New<InquireNoUnitState>{oom}(sourceFile, sourceLine) 335 .release() 336 ->ioStatementState(); 337 } 338 } 339 340 Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength, 341 const char *sourceFile, int sourceLine) { 342 Terminator oom{sourceFile, sourceLine}; 343 auto trimmed{ 344 SaveDefaultCharacter(path, TrimTrailingSpaces(path, pathLength), oom)}; 345 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(trimmed.get())}) { 346 // INQUIRE(FILE=) to a connected unit 347 return &unit->BeginIoStatement<InquireUnitState>( 348 *unit, sourceFile, sourceLine); 349 } else { 350 return &New<InquireUnconnectedFileState>{oom}( 351 std::move(trimmed), sourceFile, sourceLine) 352 .release() 353 ->ioStatementState(); 354 } 355 } 356 357 Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) { 358 Terminator oom{sourceFile, sourceLine}; 359 return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine) 360 .release() 361 ->ioStatementState(); 362 } 363 364 // Control list items 365 366 void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr, 367 bool hasEnd, bool hasEor, bool hasIoMsg) { 368 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 369 if (hasIoStat) { 370 handler.HasIoStat(); 371 } 372 if (hasErr) { 373 handler.HasErrLabel(); 374 } 375 if (hasEnd) { 376 handler.HasEndLabel(); 377 } 378 if (hasEor) { 379 handler.HasEorLabel(); 380 } 381 if (hasIoMsg) { 382 handler.HasIoMsg(); 383 } 384 } 385 386 static bool YesOrNo(const char *keyword, std::size_t length, const char *what, 387 IoErrorHandler &handler) { 388 static const char *keywords[]{"YES", "NO", nullptr}; 389 switch (IdentifyValue(keyword, length, keywords)) { 390 case 0: 391 return true; 392 case 1: 393 return false; 394 default: 395 handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what, 396 static_cast<int>(length), keyword); 397 return false; 398 } 399 } 400 401 bool IONAME(SetAdvance)( 402 Cookie cookie, const char *keyword, std::size_t length) { 403 IoStatementState &io{*cookie}; 404 ConnectionState &connection{io.GetConnectionState()}; 405 connection.nonAdvancing = 406 !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler()); 407 if (connection.nonAdvancing && connection.access == Access::Direct) { 408 io.GetIoErrorHandler().SignalError( 409 "Non-advancing I/O attempted on direct access file"); 410 } 411 return true; 412 } 413 414 bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) { 415 IoStatementState &io{*cookie}; 416 ConnectionState &connection{io.GetConnectionState()}; 417 static const char *keywords[]{"NULL", "ZERO", nullptr}; 418 switch (IdentifyValue(keyword, length, keywords)) { 419 case 0: 420 connection.modes.editingFlags &= ~blankZero; 421 return true; 422 case 1: 423 connection.modes.editingFlags |= blankZero; 424 return true; 425 default: 426 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 427 "Invalid BLANK='%.*s'", static_cast<int>(length), keyword); 428 return false; 429 } 430 } 431 432 bool IONAME(SetDecimal)( 433 Cookie cookie, const char *keyword, std::size_t length) { 434 IoStatementState &io{*cookie}; 435 ConnectionState &connection{io.GetConnectionState()}; 436 static const char *keywords[]{"COMMA", "POINT", nullptr}; 437 switch (IdentifyValue(keyword, length, keywords)) { 438 case 0: 439 connection.modes.editingFlags |= decimalComma; 440 return true; 441 case 1: 442 connection.modes.editingFlags &= ~decimalComma; 443 return true; 444 default: 445 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 446 "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword); 447 return false; 448 } 449 } 450 451 bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) { 452 IoStatementState &io{*cookie}; 453 ConnectionState &connection{io.GetConnectionState()}; 454 static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr}; 455 switch (IdentifyValue(keyword, length, keywords)) { 456 case 0: 457 connection.modes.delim = '\''; 458 return true; 459 case 1: 460 connection.modes.delim = '"'; 461 return true; 462 case 2: 463 connection.modes.delim = '\0'; 464 return true; 465 default: 466 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 467 "Invalid DELIM='%.*s'", static_cast<int>(length), keyword); 468 return false; 469 } 470 } 471 472 bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) { 473 IoStatementState &io{*cookie}; 474 ConnectionState &connection{io.GetConnectionState()}; 475 connection.modes.pad = 476 YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler()); 477 return true; 478 } 479 480 bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) { 481 IoStatementState &io{*cookie}; 482 ConnectionState &connection{io.GetConnectionState()}; 483 if (connection.access != Access::Stream) { 484 io.GetIoErrorHandler().SignalError( 485 "REC= may not appear unless ACCESS='STREAM'"); 486 return false; 487 } 488 if (pos < 1) { 489 io.GetIoErrorHandler().SignalError( 490 "POS=%zd is invalid", static_cast<std::intmax_t>(pos)); 491 return false; 492 } 493 if (auto *unit{io.GetExternalFileUnit()}) { 494 unit->SetPosition(pos); 495 return true; 496 } 497 io.GetIoErrorHandler().Crash("SetPos() on internal unit"); 498 return false; 499 } 500 501 bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) { 502 IoStatementState &io{*cookie}; 503 ConnectionState &connection{io.GetConnectionState()}; 504 if (connection.access != Access::Direct) { 505 io.GetIoErrorHandler().SignalError( 506 "REC= may not appear unless ACCESS='DIRECT'"); 507 return false; 508 } 509 if (!connection.isFixedRecordLength || !connection.recordLength) { 510 io.GetIoErrorHandler().SignalError("RECL= was not specified"); 511 return false; 512 } 513 if (rec < 1) { 514 io.GetIoErrorHandler().SignalError( 515 "REC=%zd is invalid", static_cast<std::intmax_t>(rec)); 516 return false; 517 } 518 connection.currentRecordNumber = rec; 519 if (auto *unit{io.GetExternalFileUnit()}) { 520 unit->SetPosition(rec * *connection.recordLength); 521 } 522 return true; 523 } 524 525 bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) { 526 IoStatementState &io{*cookie}; 527 ConnectionState &connection{io.GetConnectionState()}; 528 static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE", 529 "PROCESSOR_DEFINED", nullptr}; 530 switch (IdentifyValue(keyword, length, keywords)) { 531 case 0: 532 connection.modes.round = decimal::RoundUp; 533 return true; 534 case 1: 535 connection.modes.round = decimal::RoundDown; 536 return true; 537 case 2: 538 connection.modes.round = decimal::RoundToZero; 539 return true; 540 case 3: 541 connection.modes.round = decimal::RoundNearest; 542 return true; 543 case 4: 544 connection.modes.round = decimal::RoundCompatible; 545 return true; 546 case 5: 547 connection.modes.round = executionEnvironment.defaultOutputRoundingMode; 548 return true; 549 default: 550 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 551 "Invalid ROUND='%.*s'", static_cast<int>(length), keyword); 552 return false; 553 } 554 } 555 556 bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) { 557 IoStatementState &io{*cookie}; 558 ConnectionState &connection{io.GetConnectionState()}; 559 static const char *keywords[]{"PLUS", "YES", "PROCESSOR_DEFINED", nullptr}; 560 switch (IdentifyValue(keyword, length, keywords)) { 561 case 0: 562 connection.modes.editingFlags |= signPlus; 563 return true; 564 case 1: 565 case 2: // processor default is SS 566 connection.modes.editingFlags &= ~signPlus; 567 return true; 568 default: 569 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 570 "Invalid SIGN='%.*s'", static_cast<int>(length), keyword); 571 return false; 572 } 573 } 574 575 bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) { 576 IoStatementState &io{*cookie}; 577 auto *open{io.get_if<OpenStatementState>()}; 578 if (!open) { 579 io.GetIoErrorHandler().Crash( 580 "SetAccess() called when not in an OPEN statement"); 581 } 582 static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr}; 583 switch (IdentifyValue(keyword, length, keywords)) { 584 case 0: 585 open->set_access(Access::Sequential); 586 break; 587 case 1: 588 open->set_access(Access::Direct); 589 break; 590 case 2: 591 open->set_access(Access::Stream); 592 break; 593 default: 594 open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'", 595 static_cast<int>(length), keyword); 596 } 597 return true; 598 } 599 600 bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) { 601 IoStatementState &io{*cookie}; 602 auto *open{io.get_if<OpenStatementState>()}; 603 if (!open) { 604 io.GetIoErrorHandler().Crash( 605 "SetAction() called when not in an OPEN statement"); 606 } 607 std::optional<Action> action; 608 static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr}; 609 switch (IdentifyValue(keyword, length, keywords)) { 610 case 0: 611 action = Action::Read; 612 break; 613 case 1: 614 action = Action::Write; 615 break; 616 case 2: 617 action = Action::ReadWrite; 618 break; 619 default: 620 open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'", 621 static_cast<int>(length), keyword); 622 return false; 623 } 624 RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value()); 625 if (open->wasExtant()) { 626 if ((*action != Action::Write) != open->unit().mayRead() || 627 (*action != Action::Read) != open->unit().mayWrite()) { 628 open->SignalError("ACTION= may not be changed on an open unit"); 629 } 630 } 631 open->set_action(*action); 632 return true; 633 } 634 635 bool IONAME(SetAsynchronous)( 636 Cookie cookie, const char *keyword, std::size_t length) { 637 IoStatementState &io{*cookie}; 638 auto *open{io.get_if<OpenStatementState>()}; 639 if (!open) { 640 io.GetIoErrorHandler().Crash( 641 "SetAsynchronous() called when not in an OPEN statement"); 642 } 643 static const char *keywords[]{"YES", "NO", nullptr}; 644 switch (IdentifyValue(keyword, length, keywords)) { 645 case 0: 646 open->unit().set_mayAsynchronous(true); 647 return true; 648 case 1: 649 open->unit().set_mayAsynchronous(false); 650 return true; 651 default: 652 open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'", 653 static_cast<int>(length), keyword); 654 return false; 655 } 656 } 657 658 bool IONAME(SetCarriagecontrol)( 659 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 "SetCarriageControl() called when not in an OPEN statement"); 665 } 666 static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr}; 667 switch (IdentifyValue(keyword, length, keywords)) { 668 case 0: 669 return true; 670 case 1: 671 case 2: 672 open->SignalError(IostatErrorInKeyword, 673 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length), 674 keyword); 675 return false; 676 default: 677 open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'", 678 static_cast<int>(length), keyword); 679 return false; 680 } 681 } 682 683 bool IONAME(SetConvert)( 684 Cookie cookie, const char *keyword, std::size_t length) { 685 IoStatementState &io{*cookie}; 686 auto *open{io.get_if<OpenStatementState>()}; 687 if (!open) { 688 io.GetIoErrorHandler().Crash( 689 "SetConvert() called when not in an OPEN statement"); 690 } 691 if (auto convert{GetConvertFromString(keyword, length)}) { 692 open->set_convert(*convert); 693 return true; 694 } else { 695 open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'", 696 static_cast<int>(length), keyword); 697 return false; 698 } 699 } 700 701 bool IONAME(SetEncoding)( 702 Cookie cookie, const char *keyword, std::size_t length) { 703 IoStatementState &io{*cookie}; 704 auto *open{io.get_if<OpenStatementState>()}; 705 if (!open) { 706 io.GetIoErrorHandler().Crash( 707 "SetEncoding() called when not in an OPEN statement"); 708 } 709 bool isUTF8{false}; 710 static const char *keywords[]{"UTF-8", "DEFAULT", nullptr}; 711 switch (IdentifyValue(keyword, length, keywords)) { 712 case 0: 713 isUTF8 = true; 714 break; 715 case 1: 716 isUTF8 = false; 717 break; 718 default: 719 open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'", 720 static_cast<int>(length), keyword); 721 } 722 if (isUTF8 != open->unit().isUTF8) { 723 if (open->wasExtant()) { 724 open->SignalError("ENCODING= may not be changed on an open unit"); 725 } 726 open->unit().isUTF8 = isUTF8; 727 } 728 return true; 729 } 730 731 bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) { 732 IoStatementState &io{*cookie}; 733 auto *open{io.get_if<OpenStatementState>()}; 734 if (!open) { 735 io.GetIoErrorHandler().Crash( 736 "SetForm() called when not in an OPEN statement"); 737 } 738 static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr}; 739 switch (IdentifyValue(keyword, length, keywords)) { 740 case 0: 741 open->set_isUnformatted(false); 742 break; 743 case 1: 744 open->set_isUnformatted(true); 745 break; 746 default: 747 open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'", 748 static_cast<int>(length), keyword); 749 } 750 return true; 751 } 752 753 bool IONAME(SetPosition)( 754 Cookie cookie, const char *keyword, std::size_t length) { 755 IoStatementState &io{*cookie}; 756 auto *open{io.get_if<OpenStatementState>()}; 757 if (!open) { 758 io.GetIoErrorHandler().Crash( 759 "SetPosition() called when not in an OPEN statement"); 760 } 761 static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr}; 762 switch (IdentifyValue(keyword, length, positions)) { 763 case 0: 764 open->set_position(Position::AsIs); 765 return true; 766 case 1: 767 open->set_position(Position::Rewind); 768 return true; 769 case 2: 770 open->set_position(Position::Append); 771 return true; 772 default: 773 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 774 "Invalid POSITION='%.*s'", static_cast<int>(length), keyword); 775 } 776 return true; 777 } 778 779 bool IONAME(SetRecl)(Cookie cookie, std::size_t n) { 780 IoStatementState &io{*cookie}; 781 auto *open{io.get_if<OpenStatementState>()}; 782 if (!open) { 783 io.GetIoErrorHandler().Crash( 784 "SetRecl() called when not in an OPEN statement"); 785 } 786 if (n <= 0) { 787 io.GetIoErrorHandler().SignalError("RECL= must be greater than zero"); 788 } 789 if (open->wasExtant() && open->unit().isFixedRecordLength && 790 open->unit().recordLength.value_or(n) != static_cast<std::int64_t>(n)) { 791 open->SignalError("RECL= may not be changed for an open unit"); 792 } 793 open->unit().isFixedRecordLength = true; 794 open->unit().recordLength = n; 795 return true; 796 } 797 798 bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) { 799 IoStatementState &io{*cookie}; 800 if (auto *open{io.get_if<OpenStatementState>()}) { 801 static const char *statuses[]{ 802 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr}; 803 switch (IdentifyValue(keyword, length, statuses)) { 804 case 0: 805 open->set_status(OpenStatus::Old); 806 return true; 807 case 1: 808 open->set_status(OpenStatus::New); 809 return true; 810 case 2: 811 open->set_status(OpenStatus::Scratch); 812 return true; 813 case 3: 814 open->set_status(OpenStatus::Replace); 815 return true; 816 case 4: 817 open->set_status(OpenStatus::Unknown); 818 return true; 819 default: 820 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 821 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 822 } 823 return false; 824 } 825 if (auto *close{io.get_if<CloseStatementState>()}) { 826 static const char *statuses[]{"KEEP", "DELETE", nullptr}; 827 switch (IdentifyValue(keyword, length, statuses)) { 828 case 0: 829 close->set_status(CloseStatus::Keep); 830 return true; 831 case 1: 832 close->set_status(CloseStatus::Delete); 833 return true; 834 default: 835 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword, 836 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword); 837 } 838 return false; 839 } 840 if (io.get_if<NoopCloseStatementState>()) { 841 return true; // don't bother validating STATUS= in a no-op CLOSE 842 } 843 io.GetIoErrorHandler().Crash( 844 "SetStatus() called when not in an OPEN or CLOSE statement"); 845 } 846 847 bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) { 848 IoStatementState &io{*cookie}; 849 if (auto *open{io.get_if<OpenStatementState>()}) { 850 open->set_path(path, chars); 851 return true; 852 } 853 io.GetIoErrorHandler().Crash( 854 "SetFile() called when not in an OPEN statement"); 855 return false; 856 } 857 858 template <typename INT> 859 static bool SetInteger(INT &x, int kind, std::int64_t value) { 860 switch (kind) { 861 case 1: 862 reinterpret_cast<std::int8_t &>(x) = value; 863 return true; 864 case 2: 865 reinterpret_cast<std::int16_t &>(x) = value; 866 return true; 867 case 4: 868 reinterpret_cast<std::int32_t &>(x) = value; 869 return true; 870 case 8: 871 reinterpret_cast<std::int64_t &>(x) = value; 872 return true; 873 default: 874 return false; 875 } 876 } 877 878 bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) { 879 IoStatementState &io{*cookie}; 880 auto *open{io.get_if<OpenStatementState>()}; 881 if (!open) { 882 io.GetIoErrorHandler().Crash( 883 "GetNewUnit() called when not in an OPEN statement"); 884 } 885 if (!SetInteger(unit, kind, open->unit().unitNumber())) { 886 open->SignalError("GetNewUnit(): Bad INTEGER kind(%d) for result"); 887 } 888 return true; 889 } 890 891 // Data transfers 892 893 bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 894 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 895 } 896 897 bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) { 898 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 899 } 900 901 bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x, 902 std::size_t length, std::size_t elementBytes) { 903 IoStatementState &io{*cookie}; 904 if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Output>>()}) { 905 return unf->Emit(x, length, elementBytes); 906 } 907 io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O " 908 "statement that is not unformatted output"); 909 return false; 910 } 911 912 bool IONAME(InputUnformattedBlock)( 913 Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) { 914 IoStatementState &io{*cookie}; 915 io.BeginReadingRecord(); 916 if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Input>>()}) { 917 return unf->Receive(x, length, elementBytes); 918 } 919 io.GetIoErrorHandler().Crash("InputUnformattedBlock() called for an I/O " 920 "statement that is not unformatted output"); 921 return false; 922 } 923 924 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { 925 StaticDescriptor staticDescriptor; 926 Descriptor &descriptor{staticDescriptor.descriptor()}; 927 descriptor.Establish( 928 TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0); 929 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 930 } 931 932 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { 933 StaticDescriptor staticDescriptor; 934 Descriptor &descriptor{staticDescriptor.descriptor()}; 935 descriptor.Establish( 936 TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0); 937 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 938 } 939 940 bool IONAME(OutputReal32)(Cookie cookie, float x) { 941 StaticDescriptor staticDescriptor; 942 Descriptor &descriptor{staticDescriptor.descriptor()}; 943 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 944 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 945 } 946 947 bool IONAME(OutputReal64)(Cookie cookie, double x) { 948 StaticDescriptor staticDescriptor; 949 Descriptor &descriptor{staticDescriptor.descriptor()}; 950 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 951 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 952 } 953 954 bool IONAME(InputReal32)(Cookie cookie, float &x) { 955 StaticDescriptor staticDescriptor; 956 Descriptor &descriptor{staticDescriptor.descriptor()}; 957 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0); 958 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 959 } 960 961 bool IONAME(InputReal64)(Cookie cookie, double &x) { 962 StaticDescriptor staticDescriptor; 963 Descriptor &descriptor{staticDescriptor.descriptor()}; 964 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0); 965 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 966 } 967 968 bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) { 969 float z[2]{r, i}; 970 StaticDescriptor staticDescriptor; 971 Descriptor &descriptor{staticDescriptor.descriptor()}; 972 descriptor.Establish( 973 TypeCategory::Complex, 4, reinterpret_cast<void *>(&z), 0); 974 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 975 } 976 977 bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) { 978 double z[2]{r, i}; 979 StaticDescriptor staticDescriptor; 980 Descriptor &descriptor{staticDescriptor.descriptor()}; 981 descriptor.Establish( 982 TypeCategory::Complex, 8, reinterpret_cast<void *>(&z), 0); 983 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 984 } 985 986 bool IONAME(InputComplex32)(Cookie cookie, float z[2]) { 987 StaticDescriptor staticDescriptor; 988 Descriptor &descriptor{staticDescriptor.descriptor()}; 989 descriptor.Establish( 990 TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0); 991 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 992 } 993 994 bool IONAME(InputComplex64)(Cookie cookie, double z[2]) { 995 StaticDescriptor staticDescriptor; 996 Descriptor &descriptor{staticDescriptor.descriptor()}; 997 descriptor.Establish( 998 TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0); 999 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1000 } 1001 1002 bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { 1003 StaticDescriptor staticDescriptor; 1004 Descriptor &descriptor{staticDescriptor.descriptor()}; 1005 descriptor.Establish( 1006 1, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0); 1007 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1008 } 1009 1010 bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) { 1011 StaticDescriptor staticDescriptor; 1012 Descriptor &descriptor{staticDescriptor.descriptor()}; 1013 descriptor.Establish(1, length, reinterpret_cast<void *>(x), 0); 1014 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1015 } 1016 1017 bool IONAME(OutputLogical)(Cookie cookie, bool truth) { 1018 StaticDescriptor staticDescriptor; 1019 Descriptor &descriptor{staticDescriptor.descriptor()}; 1020 descriptor.Establish( 1021 TypeCategory::Logical, 1, reinterpret_cast<void *>(&truth), 0); 1022 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor); 1023 } 1024 1025 bool IONAME(InputLogical)(Cookie cookie, bool &truth) { 1026 StaticDescriptor staticDescriptor; 1027 Descriptor &descriptor{staticDescriptor.descriptor()}; 1028 descriptor.Establish( 1029 TypeCategory::Logical, 1, reinterpret_cast<void *>(&truth), 0); 1030 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor); 1031 } 1032 1033 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) { 1034 IoErrorHandler &handler{cookie->GetIoErrorHandler()}; 1035 if (handler.GetIoStat()) { // leave "msg" alone when no error 1036 handler.GetIoMsg(msg, length); 1037 } 1038 } 1039 1040 bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry, 1041 char *result, std::size_t length) { 1042 IoStatementState &io{*cookie}; 1043 return io.Inquire(inquiry, result, length); 1044 } 1045 1046 bool IONAME(InquireLogical)( 1047 Cookie cookie, InquiryKeywordHash inquiry, bool &result) { 1048 IoStatementState &io{*cookie}; 1049 return io.Inquire(inquiry, result); 1050 } 1051 1052 bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) { 1053 IoStatementState &io{*cookie}; 1054 return io.Inquire(HashInquiryKeyword("PENDING"), id, result); 1055 } 1056 1057 bool IONAME(InquireInteger64)( 1058 Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) { 1059 IoStatementState &io{*cookie}; 1060 std::int64_t n; 1061 if (io.Inquire(inquiry, n)) { 1062 SetInteger(result, kind, n); 1063 return true; 1064 } 1065 return false; 1066 } 1067 1068 enum Iostat IONAME(EndIoStatement)(Cookie cookie) { 1069 IoStatementState &io{*cookie}; 1070 return static_cast<enum Iostat>(io.EndIoStatement()); 1071 } 1072 } // namespace Fortran::runtime::io 1073