[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / flang / runtime / io-api.cpp
blob2fc530c7431a50d22ed139796a9090fef0654d64
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 //===----------------------------------------------------------------------===//
9 // Implements the I/O statement API
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>
26 namespace Fortran::runtime::io {
28 const char *InquiryKeywordHashDecode(
29 char *buffer, std::size_t n, InquiryKeywordHash hash) {
30 if (n < 1) {
31 return nullptr;
33 char *p{buffer + n};
34 *--p = '\0';
35 while (hash > 1) {
36 if (p < buffer) {
37 return nullptr;
39 *--p = 'A' + (hash % 26);
40 hash /= 26;
42 return hash == 1 ? p : nullptr;
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();
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);
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);
70 template <Direction DIR>
71 Cookie BeginInternalArrayFormattedIO(const Descriptor &descriptor,
72 const char *format, std::size_t formatLength,
73 const Descriptor *formatDescriptor, void ** /*scratchArea*/,
74 std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
75 Terminator oom{sourceFile, sourceLine};
76 return &New<InternalFormattedIoStatementState<DIR>>{oom}(descriptor, format,
77 formatLength, formatDescriptor, sourceFile, sourceLine)
78 .release()
79 ->ioStatementState();
82 Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor,
83 const char *format, std::size_t formatLength,
84 const Descriptor *formatDescriptor, void **scratchArea,
85 std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
86 return BeginInternalArrayFormattedIO<Direction::Output>(descriptor, format,
87 formatLength, formatDescriptor, scratchArea, scratchBytes, sourceFile,
88 sourceLine);
91 Cookie IONAME(BeginInternalArrayFormattedInput)(const Descriptor &descriptor,
92 const char *format, std::size_t formatLength,
93 const Descriptor *formatDescriptor, void **scratchArea,
94 std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
95 return BeginInternalArrayFormattedIO<Direction::Input>(descriptor, format,
96 formatLength, formatDescriptor, scratchArea, scratchBytes, sourceFile,
97 sourceLine);
100 template <Direction DIR>
101 Cookie BeginInternalListIO(
102 std::conditional_t<DIR == Direction::Input, const char, char> *internal,
103 std::size_t internalLength, void ** /*scratchArea*/,
104 std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
105 Terminator oom{sourceFile, sourceLine};
106 return &New<InternalListIoStatementState<DIR>>{oom}(
107 internal, internalLength, sourceFile, sourceLine)
108 .release()
109 ->ioStatementState();
112 Cookie IONAME(BeginInternalListOutput)(char *internal,
113 std::size_t internalLength, void **scratchArea, std::size_t scratchBytes,
114 const char *sourceFile, int sourceLine) {
115 return BeginInternalListIO<Direction::Output>(internal, internalLength,
116 scratchArea, scratchBytes, sourceFile, sourceLine);
119 Cookie IONAME(BeginInternalListInput)(const char *internal,
120 std::size_t internalLength, void **scratchArea, std::size_t scratchBytes,
121 const char *sourceFile, int sourceLine) {
122 return BeginInternalListIO<Direction::Input>(internal, internalLength,
123 scratchArea, scratchBytes, sourceFile, sourceLine);
126 template <Direction DIR>
127 Cookie BeginInternalFormattedIO(
128 std::conditional_t<DIR == Direction::Input, const char, char> *internal,
129 std::size_t internalLength, const char *format, std::size_t formatLength,
130 const Descriptor *formatDescriptor, void ** /*scratchArea*/,
131 std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
132 Terminator oom{sourceFile, sourceLine};
133 return &New<InternalFormattedIoStatementState<DIR>>{oom}(internal,
134 internalLength, format, formatLength, formatDescriptor, sourceFile,
135 sourceLine)
136 .release()
137 ->ioStatementState();
140 Cookie IONAME(BeginInternalFormattedOutput)(char *internal,
141 std::size_t internalLength, const char *format, std::size_t formatLength,
142 const Descriptor *formatDescriptor, void **scratchArea,
143 std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
144 return BeginInternalFormattedIO<Direction::Output>(internal, internalLength,
145 format, formatLength, formatDescriptor, scratchArea, scratchBytes,
146 sourceFile, sourceLine);
149 Cookie IONAME(BeginInternalFormattedInput)(const char *internal,
150 std::size_t internalLength, const char *format, std::size_t formatLength,
151 const Descriptor *formatDescriptor, void **scratchArea,
152 std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
153 return BeginInternalFormattedIO<Direction::Input>(internal, internalLength,
154 format, formatLength, formatDescriptor, scratchArea, scratchBytes,
155 sourceFile, sourceLine);
158 static Cookie NoopUnit(const Terminator &terminator, int unitNumber,
159 enum Iostat iostat = IostatOk) {
160 Cookie cookie{&New<NoopStatementState>{terminator}(
161 terminator.sourceFileName(), terminator.sourceLine(), unitNumber)
162 .release()
163 ->ioStatementState()};
164 if (iostat != IostatOk) {
165 cookie->GetIoErrorHandler().SetPendingError(iostat);
167 return cookie;
170 static ExternalFileUnit *GetOrCreateUnit(int unitNumber, Direction direction,
171 std::optional<bool> isUnformatted, const Terminator &terminator,
172 Cookie &errorCookie) {
173 if (ExternalFileUnit *
174 unit{ExternalFileUnit::LookUpOrCreateAnonymous(
175 unitNumber, direction, isUnformatted, terminator)}) {
176 errorCookie = nullptr;
177 return unit;
178 } else {
179 errorCookie = NoopUnit(terminator, unitNumber, IostatBadUnitNumber);
180 return nullptr;
184 template <Direction DIR, template <Direction> class STATE, typename... A>
185 Cookie BeginExternalListIO(
186 int unitNumber, const char *sourceFile, int sourceLine, A &&...xs) {
187 Terminator terminator{sourceFile, sourceLine};
188 if (unitNumber == DefaultUnit) {
189 unitNumber = DIR == Direction::Input ? 5 : 6;
191 Cookie errorCookie{nullptr};
192 ExternalFileUnit *unit{GetOrCreateUnit(
193 unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)};
194 if (!unit) {
195 return errorCookie;
197 if (!unit->isUnformatted.has_value()) {
198 unit->isUnformatted = false;
200 Iostat iostat{IostatOk};
201 if (*unit->isUnformatted) {
202 iostat = IostatFormattedIoOnUnformattedUnit;
204 if (ChildIo * child{unit->GetChildIo()}) {
205 if (iostat == IostatOk) {
206 iostat = child->CheckFormattingAndDirection(false, DIR);
208 if (iostat == IostatOk) {
209 return &child->BeginIoStatement<ChildListIoStatementState<DIR>>(
210 *child, sourceFile, sourceLine);
211 } else {
212 return &child->BeginIoStatement<ErroneousIoStatementState>(
213 iostat, nullptr /* no unit */, sourceFile, sourceLine);
215 } else {
216 if (iostat == IostatOk && unit->access == Access::Direct) {
217 iostat = IostatListIoOnDirectAccessUnit;
219 if (iostat == IostatOk) {
220 iostat = unit->SetDirection(DIR);
222 if (iostat == IostatOk) {
223 return &unit->BeginIoStatement<STATE<DIR>>(
224 terminator, std::forward<A>(xs)..., *unit, sourceFile, sourceLine);
225 } else {
226 return &unit->BeginIoStatement<ErroneousIoStatementState>(
227 terminator, iostat, unit, sourceFile, sourceLine);
232 Cookie IONAME(BeginExternalListOutput)(
233 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
234 return BeginExternalListIO<Direction::Output, ExternalListIoStatementState>(
235 unitNumber, sourceFile, sourceLine);
238 Cookie IONAME(BeginExternalListInput)(
239 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
240 return BeginExternalListIO<Direction::Input, ExternalListIoStatementState>(
241 unitNumber, sourceFile, sourceLine);
244 template <Direction DIR>
245 Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength,
246 const Descriptor *formatDescriptor, ExternalUnit unitNumber,
247 const char *sourceFile, int sourceLine) {
248 Terminator terminator{sourceFile, sourceLine};
249 if (unitNumber == DefaultUnit) {
250 unitNumber = DIR == Direction::Input ? 5 : 6;
252 Cookie errorCookie{nullptr};
253 ExternalFileUnit *unit{GetOrCreateUnit(
254 unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)};
255 if (!unit) {
256 return errorCookie;
258 Iostat iostat{IostatOk};
259 if (!unit->isUnformatted.has_value()) {
260 unit->isUnformatted = false;
262 if (*unit->isUnformatted) {
263 iostat = IostatFormattedIoOnUnformattedUnit;
265 if (ChildIo * child{unit->GetChildIo()}) {
266 if (iostat == IostatOk) {
267 iostat = child->CheckFormattingAndDirection(false, DIR);
269 if (iostat == IostatOk) {
270 return &child->BeginIoStatement<ChildFormattedIoStatementState<DIR>>(
271 *child, format, formatLength, formatDescriptor, sourceFile,
272 sourceLine);
273 } else {
274 return &child->BeginIoStatement<ErroneousIoStatementState>(
275 iostat, nullptr /* no unit */, sourceFile, sourceLine);
277 } else {
278 if (iostat == IostatOk) {
279 iostat = unit->SetDirection(DIR);
281 if (iostat == IostatOk) {
282 return &unit->BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
283 terminator, *unit, format, formatLength, formatDescriptor, sourceFile,
284 sourceLine);
285 } else {
286 return &unit->BeginIoStatement<ErroneousIoStatementState>(
287 terminator, iostat, unit, sourceFile, sourceLine);
292 Cookie IONAME(BeginExternalFormattedOutput)(const char *format,
293 std::size_t formatLength, const Descriptor *formatDescriptor,
294 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
295 return BeginExternalFormattedIO<Direction::Output>(format, formatLength,
296 formatDescriptor, unitNumber, sourceFile, sourceLine);
299 Cookie IONAME(BeginExternalFormattedInput)(const char *format,
300 std::size_t formatLength, const Descriptor *formatDescriptor,
301 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
302 return BeginExternalFormattedIO<Direction::Input>(format, formatLength,
303 formatDescriptor, unitNumber, sourceFile, sourceLine);
306 template <Direction DIR>
307 Cookie BeginUnformattedIO(
308 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
309 Terminator terminator{sourceFile, sourceLine};
310 Cookie errorCookie{nullptr};
311 ExternalFileUnit *unit{GetOrCreateUnit(
312 unitNumber, DIR, true /*unformatted*/, terminator, errorCookie)};
313 if (!unit) {
314 return errorCookie;
316 Iostat iostat{IostatOk};
317 if (!unit->isUnformatted.has_value()) {
318 unit->isUnformatted = true;
320 if (!*unit->isUnformatted) {
321 iostat = IostatUnformattedIoOnFormattedUnit;
323 if (ChildIo * child{unit->GetChildIo()}) {
324 if (iostat == IostatOk) {
325 iostat = child->CheckFormattingAndDirection(true, DIR);
327 if (iostat == IostatOk) {
328 return &child->BeginIoStatement<ChildUnformattedIoStatementState<DIR>>(
329 *child, sourceFile, sourceLine);
330 } else {
331 return &child->BeginIoStatement<ErroneousIoStatementState>(
332 iostat, nullptr /* no unit */, sourceFile, sourceLine);
334 } else {
335 if (iostat == IostatOk) {
336 iostat = unit->SetDirection(DIR);
338 if (iostat == IostatOk) {
339 IoStatementState &io{
340 unit->BeginIoStatement<ExternalUnformattedIoStatementState<DIR>>(
341 terminator, *unit, sourceFile, sourceLine)};
342 if constexpr (DIR == Direction::Output) {
343 if (unit->access == Access::Sequential) {
344 // Create space for (sub)record header to be completed by
345 // ExternalFileUnit::AdvanceRecord()
346 unit->recordLength.reset(); // in case of prior BACKSPACE
347 io.Emit("\0\0\0\0", 4); // placeholder for record length header
350 return &io;
351 } else {
352 return &unit->BeginIoStatement<ErroneousIoStatementState>(
353 terminator, iostat, unit, sourceFile, sourceLine);
358 Cookie IONAME(BeginUnformattedOutput)(
359 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
360 return BeginUnformattedIO<Direction::Output>(
361 unitNumber, sourceFile, sourceLine);
364 Cookie IONAME(BeginUnformattedInput)(
365 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
366 return BeginUnformattedIO<Direction::Input>(
367 unitNumber, sourceFile, sourceLine);
370 Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=)
371 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
372 Terminator terminator{sourceFile, sourceLine};
373 bool wasExtant{false};
374 if (ExternalFileUnit *
375 unit{ExternalFileUnit::LookUpOrCreate(
376 unitNumber, terminator, wasExtant)}) {
377 if (ChildIo * child{unit->GetChildIo()}) {
378 return &child->BeginIoStatement<ErroneousIoStatementState>(
379 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
380 sourceLine);
381 } else {
382 return &unit->BeginIoStatement<OpenStatementState>(terminator, *unit,
383 wasExtant, false /*not NEWUNIT=*/, sourceFile, sourceLine);
385 } else {
386 return NoopUnit(terminator, unitNumber, IostatBadUnitNumber);
390 Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j)
391 const char *sourceFile, int sourceLine) {
392 Terminator terminator{sourceFile, sourceLine};
393 ExternalFileUnit &unit{
394 ExternalFileUnit::NewUnit(terminator, false /*not child I/O*/)};
395 return &unit.BeginIoStatement<OpenStatementState>(terminator, unit,
396 false /*was an existing file*/, true /*NEWUNIT=*/, sourceFile,
397 sourceLine);
400 Cookie IONAME(BeginWait)(ExternalUnit unitNumber, AsynchronousId id,
401 const char *sourceFile, int sourceLine) {
402 Terminator terminator{sourceFile, sourceLine};
403 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
404 if (unit->Wait(id)) {
405 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
406 *unit, ExternalMiscIoStatementState::Wait, sourceFile, sourceLine);
407 } else {
408 return &unit->BeginIoStatement<ErroneousIoStatementState>(
409 terminator, IostatBadWaitId, unit, sourceFile, sourceLine);
411 } else {
412 return NoopUnit(
413 terminator, unitNumber, id == 0 ? IostatOk : IostatBadWaitUnit);
416 Cookie IONAME(BeginWaitAll)(
417 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
418 return IONAME(BeginWait)(unitNumber, 0 /*no ID=*/, sourceFile, sourceLine);
421 Cookie IONAME(BeginClose)(
422 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
423 Terminator terminator{sourceFile, sourceLine};
424 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
425 if (ChildIo * child{unit->GetChildIo()}) {
426 return &child->BeginIoStatement<ErroneousIoStatementState>(
427 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
428 sourceLine);
431 if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) {
432 return &unit->BeginIoStatement<CloseStatementState>(
433 terminator, *unit, sourceFile, sourceLine);
434 } else {
435 // CLOSE(UNIT=bad unit) is just a no-op
436 return NoopUnit(terminator, unitNumber);
440 Cookie IONAME(BeginFlush)(
441 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
442 Terminator terminator{sourceFile, sourceLine};
443 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
444 if (ChildIo * child{unit->GetChildIo()}) {
445 return &child->BeginIoStatement<ExternalMiscIoStatementState>(
446 *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
447 } else {
448 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
449 *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
451 } else {
452 // FLUSH(UNIT=bad unit) is an error; an unconnected unit is a no-op
453 return NoopUnit(terminator, unitNumber,
454 unitNumber >= 0 ? IostatOk : IostatBadFlushUnit);
458 Cookie IONAME(BeginBackspace)(
459 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
460 Terminator terminator{sourceFile, sourceLine};
461 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
462 if (ChildIo * child{unit->GetChildIo()}) {
463 return &child->BeginIoStatement<ErroneousIoStatementState>(
464 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
465 sourceLine);
466 } else {
467 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
468 *unit, ExternalMiscIoStatementState::Backspace, sourceFile,
469 sourceLine);
471 } else {
472 return NoopUnit(terminator, unitNumber, IostatBadBackspaceUnit);
476 Cookie IONAME(BeginEndfile)(
477 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
478 Terminator terminator{sourceFile, sourceLine};
479 Cookie errorCookie{nullptr};
480 if (ExternalFileUnit *
481 unit{GetOrCreateUnit(unitNumber, Direction::Output, std::nullopt,
482 terminator, errorCookie)}) {
483 if (ChildIo * child{unit->GetChildIo()}) {
484 return &child->BeginIoStatement<ErroneousIoStatementState>(
485 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
486 sourceLine);
487 } else {
488 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
489 *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
491 } else {
492 return errorCookie;
496 Cookie IONAME(BeginRewind)(
497 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
498 Terminator terminator{sourceFile, sourceLine};
499 Cookie errorCookie{nullptr};
500 if (ExternalFileUnit *
501 unit{GetOrCreateUnit(unitNumber, Direction::Input, std::nullopt,
502 terminator, errorCookie)}) {
503 if (ChildIo * child{unit->GetChildIo()}) {
504 return &child->BeginIoStatement<ErroneousIoStatementState>(
505 IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
506 sourceLine);
507 } else {
508 return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
509 *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
511 } else {
512 return errorCookie;
516 Cookie IONAME(BeginInquireUnit)(
517 ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
518 Terminator terminator{sourceFile, sourceLine};
519 if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
520 if (ChildIo * child{unit->GetChildIo()}) {
521 return &child->BeginIoStatement<InquireUnitState>(
522 *unit, sourceFile, sourceLine);
523 } else {
524 return &unit->BeginIoStatement<InquireUnitState>(
525 terminator, *unit, sourceFile, sourceLine);
527 } else {
528 // INQUIRE(UNIT=unrecognized unit)
529 return &New<InquireNoUnitState>{terminator}(
530 sourceFile, sourceLine, unitNumber)
531 .release()
532 ->ioStatementState();
536 Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength,
537 const char *sourceFile, int sourceLine) {
538 Terminator terminator{sourceFile, sourceLine};
539 auto trimmed{SaveDefaultCharacter(
540 path, TrimTrailingSpaces(path, pathLength), terminator)};
541 if (ExternalFileUnit *
542 unit{ExternalFileUnit::LookUp(
543 trimmed.get(), std::strlen(trimmed.get()))}) {
544 // INQUIRE(FILE=) to a connected unit
545 if (ChildIo * child{unit->GetChildIo()}) {
546 return &child->BeginIoStatement<InquireUnitState>(
547 *unit, sourceFile, sourceLine);
548 } else {
549 return &unit->BeginIoStatement<InquireUnitState>(
550 terminator, *unit, sourceFile, sourceLine);
552 } else {
553 return &New<InquireUnconnectedFileState>{terminator}(
554 std::move(trimmed), sourceFile, sourceLine)
555 .release()
556 ->ioStatementState();
560 Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) {
561 Terminator oom{sourceFile, sourceLine};
562 return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine)
563 .release()
564 ->ioStatementState();
567 // Control list items
569 void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr,
570 bool hasEnd, bool hasEor, bool hasIoMsg) {
571 IoErrorHandler &handler{cookie->GetIoErrorHandler()};
572 if (hasIoStat) {
573 handler.HasIoStat();
575 if (hasErr) {
576 handler.HasErrLabel();
578 if (hasEnd) {
579 handler.HasEndLabel();
581 if (hasEor) {
582 handler.HasEorLabel();
584 if (hasIoMsg) {
585 handler.HasIoMsg();
589 static bool YesOrNo(const char *keyword, std::size_t length, const char *what,
590 IoErrorHandler &handler) {
591 static const char *keywords[]{"YES", "NO", nullptr};
592 switch (IdentifyValue(keyword, length, keywords)) {
593 case 0:
594 return true;
595 case 1:
596 return false;
597 default:
598 handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what,
599 static_cast<int>(length), keyword);
600 return false;
604 bool IONAME(SetAdvance)(
605 Cookie cookie, const char *keyword, std::size_t length) {
606 IoStatementState &io{*cookie};
607 IoErrorHandler &handler{io.GetIoErrorHandler()};
608 bool nonAdvancing{!YesOrNo(keyword, length, "ADVANCE", handler)};
609 if (nonAdvancing && io.GetConnectionState().access == Access::Direct) {
610 handler.SignalError("Non-advancing I/O attempted on direct access file");
611 } else {
612 auto *unit{io.GetExternalFileUnit()};
613 if (unit && unit->GetChildIo()) {
614 // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3)
615 } else {
616 io.mutableModes().nonAdvancing = nonAdvancing;
619 return !handler.InError();
622 bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) {
623 IoStatementState &io{*cookie};
624 static const char *keywords[]{"NULL", "ZERO", nullptr};
625 switch (IdentifyValue(keyword, length, keywords)) {
626 case 0:
627 io.mutableModes().editingFlags &= ~blankZero;
628 return true;
629 case 1:
630 io.mutableModes().editingFlags |= blankZero;
631 return true;
632 default:
633 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
634 "Invalid BLANK='%.*s'", static_cast<int>(length), keyword);
635 return false;
639 bool IONAME(SetDecimal)(
640 Cookie cookie, const char *keyword, std::size_t length) {
641 IoStatementState &io{*cookie};
642 static const char *keywords[]{"COMMA", "POINT", nullptr};
643 switch (IdentifyValue(keyword, length, keywords)) {
644 case 0:
645 io.mutableModes().editingFlags |= decimalComma;
646 return true;
647 case 1:
648 io.mutableModes().editingFlags &= ~decimalComma;
649 return true;
650 default:
651 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
652 "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword);
653 return false;
657 bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) {
658 IoStatementState &io{*cookie};
659 static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr};
660 switch (IdentifyValue(keyword, length, keywords)) {
661 case 0:
662 io.mutableModes().delim = '\'';
663 return true;
664 case 1:
665 io.mutableModes().delim = '"';
666 return true;
667 case 2:
668 io.mutableModes().delim = '\0';
669 return true;
670 default:
671 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
672 "Invalid DELIM='%.*s'", static_cast<int>(length), keyword);
673 return false;
677 bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) {
678 IoStatementState &io{*cookie};
679 IoErrorHandler &handler{io.GetIoErrorHandler()};
680 io.mutableModes().pad = YesOrNo(keyword, length, "PAD", handler);
681 return !handler.InError();
684 bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) {
685 IoStatementState &io{*cookie};
686 IoErrorHandler &handler{io.GetIoErrorHandler()};
687 if (auto *unit{io.GetExternalFileUnit()}) {
688 return unit->SetStreamPos(pos, handler);
689 } else if (!io.get_if<ErroneousIoStatementState>()) {
690 handler.Crash("SetPos() called on internal unit");
692 return false;
695 bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) {
696 IoStatementState &io{*cookie};
697 IoErrorHandler &handler{io.GetIoErrorHandler()};
698 if (auto *unit{io.GetExternalFileUnit()}) {
699 if (unit->GetChildIo()) {
700 handler.SignalError(
701 IostatBadOpOnChildUnit, "REC= specifier on child I/O");
702 } else {
703 unit->SetDirectRec(rec, handler);
705 } else if (!io.get_if<ErroneousIoStatementState>()) {
706 handler.Crash("SetRec() called on internal unit");
708 return true;
711 bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) {
712 IoStatementState &io{*cookie};
713 static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE",
714 "PROCESSOR_DEFINED", nullptr};
715 switch (IdentifyValue(keyword, length, keywords)) {
716 case 0:
717 io.mutableModes().round = decimal::RoundUp;
718 return true;
719 case 1:
720 io.mutableModes().round = decimal::RoundDown;
721 return true;
722 case 2:
723 io.mutableModes().round = decimal::RoundToZero;
724 return true;
725 case 3:
726 io.mutableModes().round = decimal::RoundNearest;
727 return true;
728 case 4:
729 io.mutableModes().round = decimal::RoundCompatible;
730 return true;
731 case 5:
732 io.mutableModes().round = executionEnvironment.defaultOutputRoundingMode;
733 return true;
734 default:
735 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
736 "Invalid ROUND='%.*s'", static_cast<int>(length), keyword);
737 return false;
741 bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) {
742 IoStatementState &io{*cookie};
743 static const char *keywords[]{
744 "PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr};
745 switch (IdentifyValue(keyword, length, keywords)) {
746 case 0:
747 io.mutableModes().editingFlags |= signPlus;
748 return true;
749 case 1:
750 case 2: // processor default is SS
751 io.mutableModes().editingFlags &= ~signPlus;
752 return true;
753 default:
754 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
755 "Invalid SIGN='%.*s'", static_cast<int>(length), keyword);
756 return false;
760 bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) {
761 IoStatementState &io{*cookie};
762 auto *open{io.get_if<OpenStatementState>()};
763 if (!open) {
764 if (!io.get_if<ErroneousIoStatementState>()) {
765 io.GetIoErrorHandler().Crash(
766 "SetAccess() called when not in an OPEN statement");
768 return false;
769 } else if (open->completedOperation()) {
770 io.GetIoErrorHandler().Crash(
771 "SetAccess() called after GetNewUnit() for an OPEN statement");
773 static const char *keywords[]{
774 "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr};
775 switch (IdentifyValue(keyword, length, keywords)) {
776 case 0:
777 open->set_access(Access::Sequential);
778 break;
779 case 1:
780 open->set_access(Access::Direct);
781 break;
782 case 2:
783 open->set_access(Access::Stream);
784 break;
785 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND
786 open->set_position(Position::Append);
787 break;
788 default:
789 open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
790 static_cast<int>(length), keyword);
792 return true;
795 bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) {
796 IoStatementState &io{*cookie};
797 auto *open{io.get_if<OpenStatementState>()};
798 if (!open) {
799 if (!io.get_if<ErroneousIoStatementState>()) {
800 io.GetIoErrorHandler().Crash(
801 "SetAction() called when not in an OPEN statement");
803 return false;
804 } else if (open->completedOperation()) {
805 io.GetIoErrorHandler().Crash(
806 "SetAction() called after GetNewUnit() for an OPEN statement");
808 std::optional<Action> action;
809 static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr};
810 switch (IdentifyValue(keyword, length, keywords)) {
811 case 0:
812 action = Action::Read;
813 break;
814 case 1:
815 action = Action::Write;
816 break;
817 case 2:
818 action = Action::ReadWrite;
819 break;
820 default:
821 open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'",
822 static_cast<int>(length), keyword);
823 return false;
825 RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value());
826 if (open->wasExtant()) {
827 if ((*action != Action::Write) != open->unit().mayRead() ||
828 (*action != Action::Read) != open->unit().mayWrite()) {
829 open->SignalError("ACTION= may not be changed on an open unit");
832 open->set_action(*action);
833 return true;
836 bool IONAME(SetAsynchronous)(
837 Cookie cookie, const char *keyword, std::size_t length) {
838 IoStatementState &io{*cookie};
839 IoErrorHandler &handler{io.GetIoErrorHandler()};
840 bool isYes{YesOrNo(keyword, length, "ASYNCHRONOUS", handler)};
841 if (auto *open{io.get_if<OpenStatementState>()}) {
842 if (open->completedOperation()) {
843 handler.Crash(
844 "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
846 open->unit().set_mayAsynchronous(isYes);
847 } else if (auto *ext{io.get_if<ExternalIoStatementBase>()}) {
848 if (isYes) {
849 if (ext->unit().mayAsynchronous()) {
850 ext->SetAsynchronous();
851 } else {
852 handler.SignalError(IostatBadAsynchronous);
855 } else if (!io.get_if<ErroneousIoStatementState>()) {
856 handler.Crash("SetAsynchronous() called when not in an OPEN or external "
857 "I/O statement");
859 return !handler.InError();
862 bool IONAME(SetCarriagecontrol)(
863 Cookie cookie, const char *keyword, std::size_t length) {
864 IoStatementState &io{*cookie};
865 auto *open{io.get_if<OpenStatementState>()};
866 if (!open) {
867 if (!io.get_if<ErroneousIoStatementState>()) {
868 io.GetIoErrorHandler().Crash(
869 "SetCarriageControl() called when not in an OPEN statement");
871 return false;
872 } else if (open->completedOperation()) {
873 io.GetIoErrorHandler().Crash(
874 "SetCarriageControl() called after GetNewUnit() for an OPEN statement");
876 static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr};
877 switch (IdentifyValue(keyword, length, keywords)) {
878 case 0:
879 return true;
880 case 1:
881 case 2:
882 open->SignalError(IostatErrorInKeyword,
883 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length),
884 keyword);
885 return false;
886 default:
887 open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'",
888 static_cast<int>(length), keyword);
889 return false;
893 bool IONAME(SetConvert)(
894 Cookie cookie, const char *keyword, std::size_t length) {
895 IoStatementState &io{*cookie};
896 auto *open{io.get_if<OpenStatementState>()};
897 if (!open) {
898 if (!io.get_if<ErroneousIoStatementState>()) {
899 io.GetIoErrorHandler().Crash(
900 "SetConvert() called when not in an OPEN statement");
902 return false;
903 } else if (open->completedOperation()) {
904 io.GetIoErrorHandler().Crash(
905 "SetConvert() called after GetNewUnit() for an OPEN statement");
907 if (auto convert{GetConvertFromString(keyword, length)}) {
908 open->set_convert(*convert);
909 return true;
910 } else {
911 open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'",
912 static_cast<int>(length), keyword);
913 return false;
917 bool IONAME(SetEncoding)(
918 Cookie cookie, const char *keyword, std::size_t length) {
919 IoStatementState &io{*cookie};
920 auto *open{io.get_if<OpenStatementState>()};
921 if (!open) {
922 if (!io.get_if<ErroneousIoStatementState>()) {
923 io.GetIoErrorHandler().Crash(
924 "SetEncoding() called when not in an OPEN statement");
926 return false;
927 } else if (open->completedOperation()) {
928 io.GetIoErrorHandler().Crash(
929 "SetEncoding() called after GetNewUnit() for an OPEN statement");
931 // Allow the encoding to be changed on an open unit -- it's
932 // useful and safe.
933 static const char *keywords[]{"UTF-8", "DEFAULT", nullptr};
934 switch (IdentifyValue(keyword, length, keywords)) {
935 case 0:
936 open->unit().isUTF8 = true;
937 break;
938 case 1:
939 open->unit().isUTF8 = false;
940 break;
941 default:
942 open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'",
943 static_cast<int>(length), keyword);
945 return true;
948 bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
949 IoStatementState &io{*cookie};
950 auto *open{io.get_if<OpenStatementState>()};
951 if (!open) {
952 if (!io.get_if<ErroneousIoStatementState>()) {
953 io.GetIoErrorHandler().Crash(
954 "SetForm() called when not in an OPEN statement");
956 } else if (open->completedOperation()) {
957 io.GetIoErrorHandler().Crash(
958 "SetForm() called after GetNewUnit() for an OPEN statement");
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);
972 return true;
975 bool IONAME(SetPosition)(
976 Cookie cookie, const char *keyword, std::size_t length) {
977 IoStatementState &io{*cookie};
978 auto *open{io.get_if<OpenStatementState>()};
979 if (!open) {
980 if (!io.get_if<ErroneousIoStatementState>()) {
981 io.GetIoErrorHandler().Crash(
982 "SetPosition() called when not in an OPEN statement");
984 return false;
985 } else if (open->completedOperation()) {
986 io.GetIoErrorHandler().Crash(
987 "SetPosition() called after GetNewUnit() for an OPEN statement");
989 static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr};
990 switch (IdentifyValue(keyword, length, positions)) {
991 case 0:
992 open->set_position(Position::AsIs);
993 return true;
994 case 1:
995 open->set_position(Position::Rewind);
996 return true;
997 case 2:
998 open->set_position(Position::Append);
999 return true;
1000 default:
1001 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
1002 "Invalid POSITION='%.*s'", static_cast<int>(length), keyword);
1004 return true;
1007 bool IONAME(SetRecl)(Cookie cookie, std::size_t n) {
1008 IoStatementState &io{*cookie};
1009 auto *open{io.get_if<OpenStatementState>()};
1010 if (!open) {
1011 if (!io.get_if<ErroneousIoStatementState>()) {
1012 io.GetIoErrorHandler().Crash(
1013 "SetRecl() called when not in an OPEN statement");
1015 return false;
1016 } else if (open->completedOperation()) {
1017 io.GetIoErrorHandler().Crash(
1018 "SetRecl() called after GetNewUnit() for an OPEN statement");
1020 if (n <= 0) {
1021 io.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
1022 return false;
1023 } else if (open->wasExtant() &&
1024 open->unit().openRecl.value_or(0) != static_cast<std::int64_t>(n)) {
1025 open->SignalError("RECL= may not be changed for an open unit");
1026 return false;
1027 } else {
1028 open->unit().openRecl = n;
1029 return true;
1033 bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) {
1034 IoStatementState &io{*cookie};
1035 if (auto *open{io.get_if<OpenStatementState>()}) {
1036 if (open->completedOperation()) {
1037 io.GetIoErrorHandler().Crash(
1038 "SetStatus() called after GetNewUnit() for an OPEN statement");
1040 static const char *statuses[]{
1041 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr};
1042 switch (IdentifyValue(keyword, length, statuses)) {
1043 case 0:
1044 open->set_status(OpenStatus::Old);
1045 return true;
1046 case 1:
1047 open->set_status(OpenStatus::New);
1048 return true;
1049 case 2:
1050 open->set_status(OpenStatus::Scratch);
1051 return true;
1052 case 3:
1053 open->set_status(OpenStatus::Replace);
1054 return true;
1055 case 4:
1056 open->set_status(OpenStatus::Unknown);
1057 return true;
1058 default:
1059 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
1060 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword);
1062 return false;
1064 if (auto *close{io.get_if<CloseStatementState>()}) {
1065 static const char *statuses[]{"KEEP", "DELETE", nullptr};
1066 switch (IdentifyValue(keyword, length, statuses)) {
1067 case 0:
1068 close->set_status(CloseStatus::Keep);
1069 return true;
1070 case 1:
1071 close->set_status(CloseStatus::Delete);
1072 return true;
1073 default:
1074 io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
1075 "Invalid STATUS='%.*s'", static_cast<int>(length), keyword);
1077 return false;
1079 if (io.get_if<NoopStatementState>() ||
1080 io.get_if<ErroneousIoStatementState>()) {
1081 return true; // don't bother validating STATUS= in a no-op CLOSE
1083 io.GetIoErrorHandler().Crash(
1084 "SetStatus() called when not in an OPEN or CLOSE statement");
1087 bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) {
1088 IoStatementState &io{*cookie};
1089 if (auto *open{io.get_if<OpenStatementState>()}) {
1090 if (open->completedOperation()) {
1091 io.GetIoErrorHandler().Crash(
1092 "SetFile() called after GetNewUnit() for an OPEN statement");
1094 open->set_path(path, chars);
1095 return true;
1096 } else if (!io.get_if<ErroneousIoStatementState>()) {
1097 io.GetIoErrorHandler().Crash(
1098 "SetFile() called when not in an OPEN statement");
1100 return false;
1103 bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) {
1104 IoStatementState &io{*cookie};
1105 auto *open{io.get_if<OpenStatementState>()};
1106 if (!open) {
1107 if (!io.get_if<ErroneousIoStatementState>()) {
1108 io.GetIoErrorHandler().Crash(
1109 "GetNewUnit() called when not in an OPEN statement");
1111 return false;
1112 } else if (!open->InError()) {
1113 open->CompleteOperation();
1115 if (open->InError()) {
1116 // A failed OPEN(NEWUNIT=n) does not modify 'n'
1117 return false;
1119 std::int64_t result{open->unit().unitNumber()};
1120 if (!SetInteger(unit, kind, result)) {
1121 open->SignalError("GetNewUnit(): bad INTEGER kind(%d) or out-of-range "
1122 "value(%jd) for result",
1123 kind, static_cast<std::intmax_t>(result));
1125 return true;
1128 // Data transfers
1130 bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
1131 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1134 bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
1135 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1138 bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x,
1139 std::size_t length, std::size_t elementBytes) {
1140 IoStatementState &io{*cookie};
1141 if (auto *unf{io.get_if<
1142 ExternalUnformattedIoStatementState<Direction::Output>>()}) {
1143 return unf->Emit(x, length, elementBytes);
1144 } else if (auto *inq{io.get_if<InquireIOLengthState>()}) {
1145 return inq->Emit(x, length, elementBytes);
1146 } else if (!io.get_if<ErroneousIoStatementState>()) {
1147 io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O "
1148 "statement that is not unformatted output");
1150 return false;
1153 bool IONAME(InputUnformattedBlock)(
1154 Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) {
1155 IoStatementState &io{*cookie};
1156 IoErrorHandler &handler{io.GetIoErrorHandler()};
1157 io.BeginReadingRecord();
1158 if (handler.InError()) {
1159 return false;
1161 if (auto *unf{
1162 io.get_if<ExternalUnformattedIoStatementState<Direction::Input>>()}) {
1163 return unf->Receive(x, length, elementBytes);
1164 } else if (!io.get_if<ErroneousIoStatementState>()) {
1165 handler.Crash("InputUnformattedBlock() called for an I/O statement that is "
1166 "not unformatted input");
1168 return false;
1171 bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) {
1172 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger8")) {
1173 return false;
1175 StaticDescriptor staticDescriptor;
1176 Descriptor &descriptor{staticDescriptor.descriptor()};
1177 descriptor.Establish(
1178 TypeCategory::Integer, 1, reinterpret_cast<void *>(&n), 0);
1179 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1182 bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) {
1183 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger16")) {
1184 return false;
1186 StaticDescriptor staticDescriptor;
1187 Descriptor &descriptor{staticDescriptor.descriptor()};
1188 descriptor.Establish(
1189 TypeCategory::Integer, 2, reinterpret_cast<void *>(&n), 0);
1190 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1193 bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) {
1194 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger32")) {
1195 return false;
1197 StaticDescriptor staticDescriptor;
1198 Descriptor &descriptor{staticDescriptor.descriptor()};
1199 descriptor.Establish(
1200 TypeCategory::Integer, 4, reinterpret_cast<void *>(&n), 0);
1201 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1204 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) {
1205 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64")) {
1206 return false;
1208 StaticDescriptor staticDescriptor;
1209 Descriptor &descriptor{staticDescriptor.descriptor()};
1210 descriptor.Establish(
1211 TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0);
1212 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1215 #ifdef __SIZEOF_INT128__
1216 bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) {
1217 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger128")) {
1218 return false;
1220 StaticDescriptor staticDescriptor;
1221 Descriptor &descriptor{staticDescriptor.descriptor()};
1222 descriptor.Establish(
1223 TypeCategory::Integer, 16, reinterpret_cast<void *>(&n), 0);
1224 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1226 #endif
1228 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
1229 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputInteger")) {
1230 return false;
1232 StaticDescriptor staticDescriptor;
1233 Descriptor &descriptor{staticDescriptor.descriptor()};
1234 descriptor.Establish(
1235 TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0);
1236 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1239 bool IONAME(OutputReal32)(Cookie cookie, float x) {
1240 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32")) {
1241 return false;
1243 StaticDescriptor staticDescriptor;
1244 Descriptor &descriptor{staticDescriptor.descriptor()};
1245 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
1246 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1249 bool IONAME(OutputReal64)(Cookie cookie, double x) {
1250 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64")) {
1251 return false;
1253 StaticDescriptor staticDescriptor;
1254 Descriptor &descriptor{staticDescriptor.descriptor()};
1255 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
1256 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1259 bool IONAME(InputReal32)(Cookie cookie, float &x) {
1260 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputReal32")) {
1261 return false;
1263 StaticDescriptor staticDescriptor;
1264 Descriptor &descriptor{staticDescriptor.descriptor()};
1265 descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
1266 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1269 bool IONAME(InputReal64)(Cookie cookie, double &x) {
1270 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputReal64")) {
1271 return false;
1273 StaticDescriptor staticDescriptor;
1274 Descriptor &descriptor{staticDescriptor.descriptor()};
1275 descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
1276 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1279 bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) {
1280 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32")) {
1281 return false;
1283 float z[2]{r, i};
1284 StaticDescriptor staticDescriptor;
1285 Descriptor &descriptor{staticDescriptor.descriptor()};
1286 descriptor.Establish(
1287 TypeCategory::Complex, 4, reinterpret_cast<void *>(&z), 0);
1288 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1291 bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) {
1292 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64")) {
1293 return false;
1295 double z[2]{r, i};
1296 StaticDescriptor staticDescriptor;
1297 Descriptor &descriptor{staticDescriptor.descriptor()};
1298 descriptor.Establish(
1299 TypeCategory::Complex, 8, reinterpret_cast<void *>(&z), 0);
1300 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1303 bool IONAME(InputComplex32)(Cookie cookie, float z[2]) {
1304 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32")) {
1305 return false;
1307 StaticDescriptor staticDescriptor;
1308 Descriptor &descriptor{staticDescriptor.descriptor()};
1309 descriptor.Establish(
1310 TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0);
1311 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1314 bool IONAME(InputComplex64)(Cookie cookie, double z[2]) {
1315 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64")) {
1316 return false;
1318 StaticDescriptor staticDescriptor;
1319 Descriptor &descriptor{staticDescriptor.descriptor()};
1320 descriptor.Establish(
1321 TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0);
1322 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1325 bool IONAME(OutputCharacter)(
1326 Cookie cookie, const char *x, std::size_t length, int kind) {
1327 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter")) {
1328 return false;
1330 StaticDescriptor staticDescriptor;
1331 Descriptor &descriptor{staticDescriptor.descriptor()};
1332 descriptor.Establish(
1333 kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
1334 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1337 bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) {
1338 return IONAME(OutputCharacter(cookie, x, length, 1));
1341 bool IONAME(InputCharacter)(
1342 Cookie cookie, char *x, std::size_t length, int kind) {
1343 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter")) {
1344 return false;
1346 StaticDescriptor staticDescriptor;
1347 Descriptor &descriptor{staticDescriptor.descriptor()};
1348 descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0);
1349 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1352 bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) {
1353 return IONAME(InputCharacter)(cookie, x, length, 1);
1356 bool IONAME(OutputLogical)(Cookie cookie, bool truth) {
1357 if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical")) {
1358 return false;
1360 StaticDescriptor staticDescriptor;
1361 Descriptor &descriptor{staticDescriptor.descriptor()};
1362 descriptor.Establish(
1363 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
1364 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1367 bool IONAME(InputLogical)(Cookie cookie, bool &truth) {
1368 if (!cookie->CheckFormattedStmtType<Direction::Input>("InputLogical")) {
1369 return false;
1371 StaticDescriptor staticDescriptor;
1372 Descriptor &descriptor{staticDescriptor.descriptor()};
1373 descriptor.Establish(
1374 TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
1375 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1378 bool IONAME(OutputDerivedType)(Cookie cookie, const Descriptor &descriptor,
1379 const NonTbpDefinedIoTable *table) {
1380 return descr::DescriptorIO<Direction::Output>(*cookie, descriptor, table);
1383 bool IONAME(InputDerivedType)(Cookie cookie, const Descriptor &descriptor,
1384 const NonTbpDefinedIoTable *table) {
1385 return descr::DescriptorIO<Direction::Input>(*cookie, descriptor, table);
1388 std::size_t IONAME(GetSize)(Cookie cookie) {
1389 IoStatementState &io{*cookie};
1390 IoErrorHandler &handler{io.GetIoErrorHandler()};
1391 if (!handler.InError()) {
1392 io.CompleteOperation();
1394 if (const auto *formatted{
1395 io.get_if<FormattedIoStatementState<Direction::Input>>()}) {
1396 return formatted->GetEditDescriptorChars();
1397 } else if (!io.get_if<ErroneousIoStatementState>()) {
1398 handler.Crash("GetIoSize() called for an I/O statement that is not a "
1399 "formatted READ()");
1401 return 0;
1404 std::size_t IONAME(GetIoLength)(Cookie cookie) {
1405 IoStatementState &io{*cookie};
1406 IoErrorHandler &handler{io.GetIoErrorHandler()};
1407 if (!handler.InError()) {
1408 io.CompleteOperation();
1410 if (const auto *inq{io.get_if<InquireIOLengthState>()}) {
1411 return inq->bytes();
1412 } else if (!io.get_if<ErroneousIoStatementState>()) {
1413 handler.Crash("GetIoLength() called for an I/O statement that is not "
1414 "INQUIRE(IOLENGTH=)");
1416 return 0;
1419 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
1420 IoStatementState &io{*cookie};
1421 IoErrorHandler &handler{io.GetIoErrorHandler()};
1422 if (!handler.InError()) {
1423 io.CompleteOperation();
1425 if (handler.InError()) { // leave "msg" alone when no error
1426 handler.GetIoMsg(msg, length);
1430 bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry,
1431 char *result, std::size_t length) {
1432 IoStatementState &io{*cookie};
1433 return io.Inquire(inquiry, result, length);
1436 bool IONAME(InquireLogical)(
1437 Cookie cookie, InquiryKeywordHash inquiry, bool &result) {
1438 IoStatementState &io{*cookie};
1439 return io.Inquire(inquiry, result);
1442 bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) {
1443 IoStatementState &io{*cookie};
1444 return io.Inquire(HashInquiryKeyword("PENDING"), id, result);
1447 bool IONAME(InquireInteger64)(
1448 Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) {
1449 IoStatementState &io{*cookie};
1450 std::int64_t n{0}; // safe "undefined" value
1451 if (io.Inquire(inquiry, n)) {
1452 if (SetInteger(result, kind, n)) {
1453 return true;
1455 io.GetIoErrorHandler().SignalError(
1456 "InquireInteger64(): bad INTEGER kind(%d) or out-of-range "
1457 "value(%jd) for result",
1458 kind, static_cast<std::intmax_t>(n));
1460 return false;
1463 enum Iostat IONAME(EndIoStatement)(Cookie cookie) {
1464 IoStatementState &io{*cookie};
1465 return static_cast<enum Iostat>(io.EndIoStatement());
1468 template <typename INT>
1469 static enum Iostat CheckUnitNumberInRangeImpl(INT unit, bool handleError,
1470 char *ioMsg, std::size_t ioMsgLength, const char *sourceFile,
1471 int sourceLine) {
1472 static_assert(sizeof(INT) >= sizeof(ExternalUnit),
1473 "only intended to be used when the INT to ExternalUnit conversion is "
1474 "narrowing");
1475 if (unit != static_cast<ExternalUnit>(unit)) {
1476 Terminator oom{sourceFile, sourceLine};
1477 IoErrorHandler errorHandler{oom};
1478 if (handleError) {
1479 errorHandler.HasIoStat();
1480 if (ioMsg) {
1481 errorHandler.HasIoMsg();
1484 // Only provide the bad unit number in the message if SignalError can print
1485 // it accurately. Otherwise, the generic IostatUnitOverflow message will be
1486 // used.
1487 if constexpr (sizeof(INT) > sizeof(std::intmax_t)) {
1488 errorHandler.SignalError(IostatUnitOverflow);
1489 } else if (static_cast<std::intmax_t>(unit) == unit) {
1490 errorHandler.SignalError(IostatUnitOverflow,
1491 "UNIT number %jd is out of range", static_cast<std::intmax_t>(unit));
1492 } else {
1493 errorHandler.SignalError(IostatUnitOverflow);
1495 if (ioMsg) {
1496 errorHandler.GetIoMsg(ioMsg, ioMsgLength);
1498 return static_cast<enum Iostat>(errorHandler.GetIoStat());
1500 return IostatOk;
1503 enum Iostat IONAME(CheckUnitNumberInRange64)(std::int64_t unit,
1504 bool handleError, char *ioMsg, std::size_t ioMsgLength,
1505 const char *sourceFile, int sourceLine) {
1506 return CheckUnitNumberInRangeImpl(
1507 unit, handleError, ioMsg, ioMsgLength, sourceFile, sourceLine);
1510 #ifdef __SIZEOF_INT128__
1511 enum Iostat IONAME(CheckUnitNumberInRange128)(common::int128_t unit,
1512 bool handleError, char *ioMsg, std::size_t ioMsgLength,
1513 const char *sourceFile, int sourceLine) {
1514 return CheckUnitNumberInRangeImpl(
1515 unit, handleError, ioMsg, ioMsgLength, sourceFile, sourceLine);
1517 #endif
1519 } // namespace Fortran::runtime::io
1521 #if defined(_LIBCPP_VERBOSE_ABORT)
1522 // Provide own definition for `std::__libcpp_verbose_abort` to avoid dependency
1523 // on the version provided by libc++.
1525 void std::__libcpp_verbose_abort(char const *format, ...) {
1526 va_list list;
1527 va_start(list, format);
1528 std::vfprintf(stderr, format, list);
1529 va_end(list);
1531 std::abort();
1533 #endif