1 //===-- runtime/io-api.cpp ------------------------------------------------===//
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
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"
18 #include "terminator.h"
21 #include "flang/Runtime/descriptor.h"
22 #include "flang/Runtime/memory.h"
26 namespace Fortran::runtime::io
{
28 const char *InquiryKeywordHashDecode(
29 char *buffer
, std::size_t n
, InquiryKeywordHash hash
) {
39 *--p
= 'A' + (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
)
56 Cookie
IONAME(BeginInternalArrayListOutput
)(const Descriptor
&descriptor
,
57 void **scratchArea
, std::size_t scratchBytes
, const char *sourceFile
,
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
,
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
)
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
,
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
,
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
)
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
,
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
)
163 ->ioStatementState()};
164 if (iostat
!= IostatOk
) {
165 cookie
->GetIoErrorHandler().SetPendingError(iostat
);
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;
179 errorCookie
= NoopUnit(terminator
, unitNumber
, IostatBadUnitNumber
);
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
)};
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
);
212 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
213 iostat
, nullptr /* no unit */, sourceFile
, sourceLine
);
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
);
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
)};
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
,
274 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
275 iostat
, nullptr /* no unit */, sourceFile
, sourceLine
);
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
,
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
)};
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
);
331 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
332 iostat
, nullptr /* no unit */, sourceFile
, sourceLine
);
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
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
,
382 return &unit
->BeginIoStatement
<OpenStatementState
>(terminator
, *unit
,
383 wasExtant
, false /*not NEWUNIT=*/, sourceFile
, sourceLine
);
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
,
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
);
408 return &unit
->BeginIoStatement
<ErroneousIoStatementState
>(
409 terminator
, IostatBadWaitId
, unit
, sourceFile
, sourceLine
);
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
,
431 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUpForClose(unitNumber
)}) {
432 return &unit
->BeginIoStatement
<CloseStatementState
>(
433 terminator
, *unit
, sourceFile
, sourceLine
);
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
);
448 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
449 *unit
, ExternalMiscIoStatementState::Flush
, sourceFile
, sourceLine
);
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
,
467 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
468 *unit
, ExternalMiscIoStatementState::Backspace
, sourceFile
,
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
,
488 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
489 *unit
, ExternalMiscIoStatementState::Endfile
, sourceFile
, sourceLine
);
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
,
508 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
509 *unit
, ExternalMiscIoStatementState::Rewind
, sourceFile
, sourceLine
);
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
);
524 return &unit
->BeginIoStatement
<InquireUnitState
>(
525 terminator
, *unit
, sourceFile
, sourceLine
);
528 // INQUIRE(UNIT=unrecognized unit)
529 return &New
<InquireNoUnitState
>{terminator
}(
530 sourceFile
, sourceLine
, unitNumber
)
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
);
549 return &unit
->BeginIoStatement
<InquireUnitState
>(
550 terminator
, *unit
, sourceFile
, sourceLine
);
553 return &New
<InquireUnconnectedFileState
>{terminator
}(
554 std::move(trimmed
), sourceFile
, sourceLine
)
556 ->ioStatementState();
560 Cookie
IONAME(BeginInquireIoLength
)(const char *sourceFile
, int sourceLine
) {
561 Terminator oom
{sourceFile
, sourceLine
};
562 return &New
<InquireIOLengthState
>{oom
}(sourceFile
, sourceLine
)
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()};
576 handler
.HasErrLabel();
579 handler
.HasEndLabel();
582 handler
.HasEorLabel();
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
)) {
598 handler
.SignalError(IostatErrorInKeyword
, "Invalid %s='%.*s'", what
,
599 static_cast<int>(length
), keyword
);
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");
612 auto *unit
{io
.GetExternalFileUnit()};
613 if (unit
&& unit
->GetChildIo()) {
614 // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3)
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
)) {
627 io
.mutableModes().editingFlags
&= ~blankZero
;
630 io
.mutableModes().editingFlags
|= blankZero
;
633 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
634 "Invalid BLANK='%.*s'", static_cast<int>(length
), keyword
);
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
)) {
645 io
.mutableModes().editingFlags
|= decimalComma
;
648 io
.mutableModes().editingFlags
&= ~decimalComma
;
651 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
652 "Invalid DECIMAL='%.*s'", static_cast<int>(length
), keyword
);
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
)) {
662 io
.mutableModes().delim
= '\'';
665 io
.mutableModes().delim
= '"';
668 io
.mutableModes().delim
= '\0';
671 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
672 "Invalid DELIM='%.*s'", static_cast<int>(length
), keyword
);
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");
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()) {
701 IostatBadOpOnChildUnit
, "REC= specifier on child I/O");
703 unit
->SetDirectRec(rec
, handler
);
705 } else if (!io
.get_if
<ErroneousIoStatementState
>()) {
706 handler
.Crash("SetRec() called on internal unit");
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
)) {
717 io
.mutableModes().round
= decimal::RoundUp
;
720 io
.mutableModes().round
= decimal::RoundDown
;
723 io
.mutableModes().round
= decimal::RoundToZero
;
726 io
.mutableModes().round
= decimal::RoundNearest
;
729 io
.mutableModes().round
= decimal::RoundCompatible
;
732 io
.mutableModes().round
= executionEnvironment
.defaultOutputRoundingMode
;
735 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
736 "Invalid ROUND='%.*s'", static_cast<int>(length
), keyword
);
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
)) {
747 io
.mutableModes().editingFlags
|= signPlus
;
750 case 2: // processor default is SS
751 io
.mutableModes().editingFlags
&= ~signPlus
;
754 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
755 "Invalid SIGN='%.*s'", static_cast<int>(length
), keyword
);
760 bool IONAME(SetAccess
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
761 IoStatementState
&io
{*cookie
};
762 auto *open
{io
.get_if
<OpenStatementState
>()};
764 if (!io
.get_if
<ErroneousIoStatementState
>()) {
765 io
.GetIoErrorHandler().Crash(
766 "SetAccess() called when not in an OPEN statement");
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
)) {
777 open
->set_access(Access::Sequential
);
780 open
->set_access(Access::Direct
);
783 open
->set_access(Access::Stream
);
785 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND
786 open
->set_position(Position::Append
);
789 open
->SignalError(IostatErrorInKeyword
, "Invalid ACCESS='%.*s'",
790 static_cast<int>(length
), keyword
);
795 bool IONAME(SetAction
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
796 IoStatementState
&io
{*cookie
};
797 auto *open
{io
.get_if
<OpenStatementState
>()};
799 if (!io
.get_if
<ErroneousIoStatementState
>()) {
800 io
.GetIoErrorHandler().Crash(
801 "SetAction() called when not in an OPEN statement");
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
)) {
812 action
= Action::Read
;
815 action
= Action::Write
;
818 action
= Action::ReadWrite
;
821 open
->SignalError(IostatErrorInKeyword
, "Invalid ACTION='%.*s'",
822 static_cast<int>(length
), keyword
);
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
);
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()) {
844 "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
846 open
->unit().set_mayAsynchronous(isYes
);
847 } else if (auto *ext
{io
.get_if
<ExternalIoStatementBase
>()}) {
849 if (ext
->unit().mayAsynchronous()) {
850 ext
->SetAsynchronous();
852 handler
.SignalError(IostatBadAsynchronous
);
855 } else if (!io
.get_if
<ErroneousIoStatementState
>()) {
856 handler
.Crash("SetAsynchronous() called when not in an OPEN or external "
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
>()};
867 if (!io
.get_if
<ErroneousIoStatementState
>()) {
868 io
.GetIoErrorHandler().Crash(
869 "SetCarriageControl() called when not in an OPEN statement");
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
)) {
882 open
->SignalError(IostatErrorInKeyword
,
883 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length
),
887 open
->SignalError(IostatErrorInKeyword
, "Invalid CARRIAGECONTROL='%.*s'",
888 static_cast<int>(length
), keyword
);
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
>()};
898 if (!io
.get_if
<ErroneousIoStatementState
>()) {
899 io
.GetIoErrorHandler().Crash(
900 "SetConvert() called when not in an OPEN statement");
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
);
911 open
->SignalError(IostatErrorInKeyword
, "Invalid CONVERT='%.*s'",
912 static_cast<int>(length
), keyword
);
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
>()};
922 if (!io
.get_if
<ErroneousIoStatementState
>()) {
923 io
.GetIoErrorHandler().Crash(
924 "SetEncoding() called when not in an OPEN statement");
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
933 static const char *keywords
[]{"UTF-8", "DEFAULT", nullptr};
934 switch (IdentifyValue(keyword
, length
, keywords
)) {
936 open
->unit().isUTF8
= true;
939 open
->unit().isUTF8
= false;
942 open
->SignalError(IostatErrorInKeyword
, "Invalid ENCODING='%.*s'",
943 static_cast<int>(length
), keyword
);
948 bool IONAME(SetForm
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
949 IoStatementState
&io
{*cookie
};
950 auto *open
{io
.get_if
<OpenStatementState
>()};
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
)) {
963 open
->set_isUnformatted(false);
966 open
->set_isUnformatted(true);
969 open
->SignalError(IostatErrorInKeyword
, "Invalid FORM='%.*s'",
970 static_cast<int>(length
), keyword
);
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
>()};
980 if (!io
.get_if
<ErroneousIoStatementState
>()) {
981 io
.GetIoErrorHandler().Crash(
982 "SetPosition() called when not in an OPEN statement");
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
)) {
992 open
->set_position(Position::AsIs
);
995 open
->set_position(Position::Rewind
);
998 open
->set_position(Position::Append
);
1001 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
1002 "Invalid POSITION='%.*s'", static_cast<int>(length
), keyword
);
1007 bool IONAME(SetRecl
)(Cookie cookie
, std::size_t n
) {
1008 IoStatementState
&io
{*cookie
};
1009 auto *open
{io
.get_if
<OpenStatementState
>()};
1011 if (!io
.get_if
<ErroneousIoStatementState
>()) {
1012 io
.GetIoErrorHandler().Crash(
1013 "SetRecl() called when not in an OPEN statement");
1016 } else if (open
->completedOperation()) {
1017 io
.GetIoErrorHandler().Crash(
1018 "SetRecl() called after GetNewUnit() for an OPEN statement");
1021 io
.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
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");
1028 open
->unit().openRecl
= n
;
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
)) {
1044 open
->set_status(OpenStatus::Old
);
1047 open
->set_status(OpenStatus::New
);
1050 open
->set_status(OpenStatus::Scratch
);
1053 open
->set_status(OpenStatus::Replace
);
1056 open
->set_status(OpenStatus::Unknown
);
1059 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
1060 "Invalid STATUS='%.*s'", static_cast<int>(length
), keyword
);
1064 if (auto *close
{io
.get_if
<CloseStatementState
>()}) {
1065 static const char *statuses
[]{"KEEP", "DELETE", nullptr};
1066 switch (IdentifyValue(keyword
, length
, statuses
)) {
1068 close
->set_status(CloseStatus::Keep
);
1071 close
->set_status(CloseStatus::Delete
);
1074 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
1075 "Invalid STATUS='%.*s'", static_cast<int>(length
), keyword
);
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
);
1096 } else if (!io
.get_if
<ErroneousIoStatementState
>()) {
1097 io
.GetIoErrorHandler().Crash(
1098 "SetFile() called when not in an OPEN statement");
1103 bool IONAME(GetNewUnit
)(Cookie cookie
, int &unit
, int kind
) {
1104 IoStatementState
&io
{*cookie
};
1105 auto *open
{io
.get_if
<OpenStatementState
>()};
1107 if (!io
.get_if
<ErroneousIoStatementState
>()) {
1108 io
.GetIoErrorHandler().Crash(
1109 "GetNewUnit() called when not in an OPEN statement");
1112 } else if (!open
->InError()) {
1113 open
->CompleteOperation();
1115 if (open
->InError()) {
1116 // A failed OPEN(NEWUNIT=n) does not modify 'n'
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
));
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");
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()) {
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");
1171 bool IONAME(OutputInteger8
)(Cookie cookie
, std::int8_t n
) {
1172 if (!cookie
->CheckFormattedStmtType
<Direction::Output
>("OutputInteger8")) {
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")) {
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")) {
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")) {
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")) {
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
);
1228 bool IONAME(InputInteger
)(Cookie cookie
, std::int64_t &n
, int kind
) {
1229 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputInteger")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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()");
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=)");
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
)) {
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
));
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
,
1472 static_assert(sizeof(INT
) >= sizeof(ExternalUnit
),
1473 "only intended to be used when the INT to ExternalUnit conversion is "
1475 if (unit
!= static_cast<ExternalUnit
>(unit
)) {
1476 Terminator oom
{sourceFile
, sourceLine
};
1477 IoErrorHandler errorHandler
{oom
};
1479 errorHandler
.HasIoStat();
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
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
));
1493 errorHandler
.SignalError(IostatUnitOverflow
);
1496 errorHandler
.GetIoMsg(ioMsg
, ioMsgLength
);
1498 return static_cast<enum Iostat
>(errorHandler
.GetIoStat());
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
);
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
, ...) {
1527 va_start(list
, format
);
1528 std::vfprintf(stderr
, format
, list
);