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 // template function BeginExternalListIo<> is in runtime/io-api-common.h.
12 // APIs BeginExternalListOutput, OutputInteger{8,16,32,64,128},
13 // OutputReal{32,64}, OutputComplex{32,64}, OutputAscii, & EndIoStatement()
14 // are in runtime/io-api-minimal.cpp.
16 #include "flang/Runtime/io-api.h"
17 #include "descriptor-io.h"
18 #include "edit-input.h"
19 #include "edit-output.h"
20 #include "environment.h"
22 #include "io-api-common.h"
24 #include "terminator.h"
27 #include "flang/Common/optional.h"
28 #include "flang/Runtime/descriptor.h"
29 #include "flang/Runtime/memory.h"
33 namespace Fortran::runtime::io
{
34 RT_EXT_API_GROUP_BEGIN
36 RT_API_ATTRS
const char *InquiryKeywordHashDecode(
37 char *buffer
, std::size_t n
, InquiryKeywordHash hash
) {
47 *--p
= 'A' + (hash
% 26);
50 return hash
== 1 ? p
: nullptr;
53 template <Direction
DIR>
54 RT_API_ATTRS Cookie
BeginInternalArrayListIO(const Descriptor
&descriptor
,
55 void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
56 const char *sourceFile
, int sourceLine
) {
57 Terminator oom
{sourceFile
, sourceLine
};
58 return &New
<InternalListIoStatementState
<DIR>>{oom
}(
59 descriptor
, sourceFile
, sourceLine
)
64 Cookie
IODEF(BeginInternalArrayListOutput
)(const Descriptor
&descriptor
,
65 void **scratchArea
, std::size_t scratchBytes
, const char *sourceFile
,
67 return BeginInternalArrayListIO
<Direction::Output
>(
68 descriptor
, scratchArea
, scratchBytes
, sourceFile
, sourceLine
);
71 Cookie
IODEF(BeginInternalArrayListInput
)(const Descriptor
&descriptor
,
72 void **scratchArea
, std::size_t scratchBytes
, const char *sourceFile
,
74 return BeginInternalArrayListIO
<Direction::Input
>(
75 descriptor
, scratchArea
, scratchBytes
, sourceFile
, sourceLine
);
78 template <Direction
DIR>
79 RT_API_ATTRS Cookie
BeginInternalArrayFormattedIO(const Descriptor
&descriptor
,
80 const char *format
, std::size_t formatLength
,
81 const Descriptor
*formatDescriptor
, void ** /*scratchArea*/,
82 std::size_t /*scratchBytes*/, const char *sourceFile
, int sourceLine
) {
83 Terminator oom
{sourceFile
, sourceLine
};
84 return &New
<InternalFormattedIoStatementState
<DIR>>{oom
}(descriptor
, format
,
85 formatLength
, formatDescriptor
, sourceFile
, sourceLine
)
90 Cookie
IODEF(BeginInternalArrayFormattedOutput
)(const Descriptor
&descriptor
,
91 const char *format
, std::size_t formatLength
,
92 const Descriptor
*formatDescriptor
, void **scratchArea
,
93 std::size_t scratchBytes
, const char *sourceFile
, int sourceLine
) {
94 return BeginInternalArrayFormattedIO
<Direction::Output
>(descriptor
, format
,
95 formatLength
, formatDescriptor
, scratchArea
, scratchBytes
, sourceFile
,
99 Cookie
IODEF(BeginInternalArrayFormattedInput
)(const Descriptor
&descriptor
,
100 const char *format
, std::size_t formatLength
,
101 const Descriptor
*formatDescriptor
, void **scratchArea
,
102 std::size_t scratchBytes
, const char *sourceFile
, int sourceLine
) {
103 return BeginInternalArrayFormattedIO
<Direction::Input
>(descriptor
, format
,
104 formatLength
, formatDescriptor
, scratchArea
, scratchBytes
, sourceFile
,
108 template <Direction
DIR>
109 RT_API_ATTRS Cookie
BeginInternalListIO(
110 std::conditional_t
<DIR == Direction::Input
, const char, char> *internal
,
111 std::size_t internalLength
, void ** /*scratchArea*/,
112 std::size_t /*scratchBytes*/, const char *sourceFile
, int sourceLine
) {
113 Terminator oom
{sourceFile
, sourceLine
};
114 return &New
<InternalListIoStatementState
<DIR>>{oom
}(
115 internal
, internalLength
, sourceFile
, sourceLine
)
117 ->ioStatementState();
120 Cookie
IODEF(BeginInternalListOutput
)(char *internal
,
121 std::size_t internalLength
, void **scratchArea
, std::size_t scratchBytes
,
122 const char *sourceFile
, int sourceLine
) {
123 return BeginInternalListIO
<Direction::Output
>(internal
, internalLength
,
124 scratchArea
, scratchBytes
, sourceFile
, sourceLine
);
127 Cookie
IODEF(BeginInternalListInput
)(const char *internal
,
128 std::size_t internalLength
, void **scratchArea
, std::size_t scratchBytes
,
129 const char *sourceFile
, int sourceLine
) {
130 return BeginInternalListIO
<Direction::Input
>(internal
, internalLength
,
131 scratchArea
, scratchBytes
, sourceFile
, sourceLine
);
134 template <Direction
DIR>
135 RT_API_ATTRS Cookie
BeginInternalFormattedIO(
136 std::conditional_t
<DIR == Direction::Input
, const char, char> *internal
,
137 std::size_t internalLength
, const char *format
, std::size_t formatLength
,
138 const Descriptor
*formatDescriptor
, void ** /*scratchArea*/,
139 std::size_t /*scratchBytes*/, const char *sourceFile
, int sourceLine
) {
140 Terminator oom
{sourceFile
, sourceLine
};
141 return &New
<InternalFormattedIoStatementState
<DIR>>{oom
}(internal
,
142 internalLength
, format
, formatLength
, formatDescriptor
, sourceFile
,
145 ->ioStatementState();
148 Cookie
IODEF(BeginInternalFormattedOutput
)(char *internal
,
149 std::size_t internalLength
, const char *format
, std::size_t formatLength
,
150 const Descriptor
*formatDescriptor
, void **scratchArea
,
151 std::size_t scratchBytes
, const char *sourceFile
, int sourceLine
) {
152 return BeginInternalFormattedIO
<Direction::Output
>(internal
, internalLength
,
153 format
, formatLength
, formatDescriptor
, scratchArea
, scratchBytes
,
154 sourceFile
, sourceLine
);
157 Cookie
IODEF(BeginInternalFormattedInput
)(const char *internal
,
158 std::size_t internalLength
, const char *format
, std::size_t formatLength
,
159 const Descriptor
*formatDescriptor
, void **scratchArea
,
160 std::size_t scratchBytes
, const char *sourceFile
, int sourceLine
) {
161 return BeginInternalFormattedIO
<Direction::Input
>(internal
, internalLength
,
162 format
, formatLength
, formatDescriptor
, scratchArea
, scratchBytes
,
163 sourceFile
, sourceLine
);
166 Cookie
IODEF(BeginExternalListInput
)(
167 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
168 return BeginExternalListIO
<Direction::Input
, ExternalListIoStatementState
>(
169 unitNumber
, sourceFile
, sourceLine
);
172 template <Direction
DIR>
173 RT_API_ATTRS Cookie
BeginExternalFormattedIO(const char *format
,
174 std::size_t formatLength
, const Descriptor
*formatDescriptor
,
175 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
176 Terminator terminator
{sourceFile
, sourceLine
};
177 Cookie errorCookie
{nullptr};
178 ExternalFileUnit
*unit
{GetOrCreateUnit(
179 unitNumber
, DIR, false /*!unformatted*/, terminator
, errorCookie
)};
183 Iostat iostat
{IostatOk
};
184 if (!unit
->isUnformatted
.has_value()) {
185 unit
->isUnformatted
= false;
187 if (*unit
->isUnformatted
) {
188 iostat
= IostatFormattedIoOnUnformattedUnit
;
190 if (ChildIo
* child
{unit
->GetChildIo()}) {
191 if (iostat
== IostatOk
) {
192 iostat
= child
->CheckFormattingAndDirection(false, DIR);
194 if (iostat
== IostatOk
) {
195 return &child
->BeginIoStatement
<ChildFormattedIoStatementState
<DIR>>(
196 *child
, format
, formatLength
, formatDescriptor
, sourceFile
,
199 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
200 iostat
, nullptr /* no unit */, sourceFile
, sourceLine
);
203 if (iostat
== IostatOk
) {
204 iostat
= unit
->SetDirection(DIR);
206 if (iostat
== IostatOk
) {
207 return &unit
->BeginIoStatement
<ExternalFormattedIoStatementState
<DIR>>(
208 terminator
, *unit
, format
, formatLength
, formatDescriptor
, sourceFile
,
211 return &unit
->BeginIoStatement
<ErroneousIoStatementState
>(
212 terminator
, iostat
, unit
, sourceFile
, sourceLine
);
217 Cookie
IODEF(BeginExternalFormattedOutput
)(const char *format
,
218 std::size_t formatLength
, const Descriptor
*formatDescriptor
,
219 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
220 return BeginExternalFormattedIO
<Direction::Output
>(format
, formatLength
,
221 formatDescriptor
, unitNumber
, sourceFile
, sourceLine
);
224 Cookie
IODEF(BeginExternalFormattedInput
)(const char *format
,
225 std::size_t formatLength
, const Descriptor
*formatDescriptor
,
226 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
227 return BeginExternalFormattedIO
<Direction::Input
>(format
, formatLength
,
228 formatDescriptor
, unitNumber
, sourceFile
, sourceLine
);
231 template <Direction
DIR>
232 RT_API_ATTRS Cookie
BeginUnformattedIO(
233 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
234 Terminator terminator
{sourceFile
, sourceLine
};
235 Cookie errorCookie
{nullptr};
236 ExternalFileUnit
*unit
{GetOrCreateUnit(
237 unitNumber
, DIR, true /*unformatted*/, terminator
, errorCookie
)};
241 Iostat iostat
{IostatOk
};
242 if (!unit
->isUnformatted
.has_value()) {
243 unit
->isUnformatted
= true;
245 if (!*unit
->isUnformatted
) {
246 iostat
= IostatUnformattedIoOnFormattedUnit
;
248 if (ChildIo
* child
{unit
->GetChildIo()}) {
249 if (iostat
== IostatOk
) {
250 iostat
= child
->CheckFormattingAndDirection(true, DIR);
252 if (iostat
== IostatOk
) {
253 return &child
->BeginIoStatement
<ChildUnformattedIoStatementState
<DIR>>(
254 *child
, sourceFile
, sourceLine
);
256 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
257 iostat
, nullptr /* no unit */, sourceFile
, sourceLine
);
260 if (iostat
== IostatOk
) {
261 iostat
= unit
->SetDirection(DIR);
263 if (iostat
== IostatOk
) {
264 IoStatementState
&io
{
265 unit
->BeginIoStatement
<ExternalUnformattedIoStatementState
<DIR>>(
266 terminator
, *unit
, sourceFile
, sourceLine
)};
267 if constexpr (DIR == Direction::Output
) {
268 if (unit
->access
== Access::Sequential
) {
269 // Create space for (sub)record header to be completed by
270 // ExternalFileUnit::AdvanceRecord()
271 unit
->recordLength
.reset(); // in case of prior BACKSPACE
272 io
.Emit("\0\0\0\0", 4); // placeholder for record length header
277 return &unit
->BeginIoStatement
<ErroneousIoStatementState
>(
278 terminator
, iostat
, unit
, sourceFile
, sourceLine
);
283 Cookie
IODEF(BeginUnformattedOutput
)(
284 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
285 return BeginUnformattedIO
<Direction::Output
>(
286 unitNumber
, sourceFile
, sourceLine
);
289 Cookie
IODEF(BeginUnformattedInput
)(
290 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
291 return BeginUnformattedIO
<Direction::Input
>(
292 unitNumber
, sourceFile
, sourceLine
);
295 Cookie
IODEF(BeginOpenUnit
)( // OPEN(without NEWUNIT=)
296 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
297 Terminator terminator
{sourceFile
, sourceLine
};
298 bool wasExtant
{false};
299 if (ExternalFileUnit
*
300 unit
{ExternalFileUnit::LookUpOrCreate(
301 unitNumber
, terminator
, wasExtant
)}) {
302 if (ChildIo
* child
{unit
->GetChildIo()}) {
303 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
304 IostatBadOpOnChildUnit
, nullptr /* no unit */, sourceFile
,
307 return &unit
->BeginIoStatement
<OpenStatementState
>(terminator
, *unit
,
308 wasExtant
, false /*not NEWUNIT=*/, sourceFile
, sourceLine
);
311 return NoopUnit(terminator
, unitNumber
, IostatBadUnitNumber
);
315 Cookie
IODEF(BeginOpenNewUnit
)( // OPEN(NEWUNIT=j)
316 const char *sourceFile
, int sourceLine
) {
317 Terminator terminator
{sourceFile
, sourceLine
};
318 ExternalFileUnit
&unit
{
319 ExternalFileUnit::NewUnit(terminator
, false /*not child I/O*/)};
320 return &unit
.BeginIoStatement
<OpenStatementState
>(terminator
, unit
,
321 false /*was an existing file*/, true /*NEWUNIT=*/, sourceFile
,
325 Cookie
IODEF(BeginWait
)(ExternalUnit unitNumber
, AsynchronousId id
,
326 const char *sourceFile
, int sourceLine
) {
327 Terminator terminator
{sourceFile
, sourceLine
};
328 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUp(unitNumber
)}) {
329 if (unit
->Wait(id
)) {
330 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
331 *unit
, ExternalMiscIoStatementState::Wait
, sourceFile
, sourceLine
);
333 return &unit
->BeginIoStatement
<ErroneousIoStatementState
>(
334 terminator
, IostatBadWaitId
, unit
, sourceFile
, sourceLine
);
338 terminator
, unitNumber
, id
== 0 ? IostatOk
: IostatBadWaitUnit
);
341 Cookie
IODEF(BeginWaitAll
)(
342 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
343 return IONAME(BeginWait
)(unitNumber
, 0 /*no ID=*/, sourceFile
, sourceLine
);
346 Cookie
IODEF(BeginClose
)(
347 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
348 Terminator terminator
{sourceFile
, sourceLine
};
349 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUp(unitNumber
)}) {
350 if (ChildIo
* child
{unit
->GetChildIo()}) {
351 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
352 IostatBadOpOnChildUnit
, nullptr /* no unit */, sourceFile
,
356 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUpForClose(unitNumber
)}) {
357 return &unit
->BeginIoStatement
<CloseStatementState
>(
358 terminator
, *unit
, sourceFile
, sourceLine
);
360 // CLOSE(UNIT=bad unit) is just a no-op
361 return NoopUnit(terminator
, unitNumber
);
365 Cookie
IODEF(BeginFlush
)(
366 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
367 Terminator terminator
{sourceFile
, sourceLine
};
368 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUp(unitNumber
)}) {
369 if (ChildIo
* child
{unit
->GetChildIo()}) {
370 return &child
->BeginIoStatement
<ExternalMiscIoStatementState
>(
371 *unit
, ExternalMiscIoStatementState::Flush
, sourceFile
, sourceLine
);
373 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
374 *unit
, ExternalMiscIoStatementState::Flush
, sourceFile
, sourceLine
);
377 // FLUSH(UNIT=bad unit) is an error; an unconnected unit is a no-op
378 return NoopUnit(terminator
, unitNumber
,
379 unitNumber
>= 0 ? IostatOk
: IostatBadFlushUnit
);
383 Cookie
IODEF(BeginBackspace
)(
384 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
385 Terminator terminator
{sourceFile
, sourceLine
};
386 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUp(unitNumber
)}) {
387 if (ChildIo
* child
{unit
->GetChildIo()}) {
388 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
389 IostatBadOpOnChildUnit
, nullptr /* no unit */, sourceFile
,
392 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
393 *unit
, ExternalMiscIoStatementState::Backspace
, sourceFile
,
397 return NoopUnit(terminator
, unitNumber
, IostatBadBackspaceUnit
);
401 Cookie
IODEF(BeginEndfile
)(
402 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
403 Terminator terminator
{sourceFile
, sourceLine
};
404 Cookie errorCookie
{nullptr};
405 if (ExternalFileUnit
*
406 unit
{GetOrCreateUnit(unitNumber
, Direction::Output
,
407 Fortran::common::nullopt
, terminator
, errorCookie
)}) {
408 if (ChildIo
* child
{unit
->GetChildIo()}) {
409 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
410 IostatBadOpOnChildUnit
, nullptr /* no unit */, sourceFile
,
413 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
414 *unit
, ExternalMiscIoStatementState::Endfile
, sourceFile
, sourceLine
);
421 Cookie
IODEF(BeginRewind
)(
422 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
423 Terminator terminator
{sourceFile
, sourceLine
};
424 Cookie errorCookie
{nullptr};
425 if (ExternalFileUnit
*
426 unit
{GetOrCreateUnit(unitNumber
, Direction::Input
,
427 Fortran::common::nullopt
, terminator
, errorCookie
)}) {
428 if (ChildIo
* child
{unit
->GetChildIo()}) {
429 return &child
->BeginIoStatement
<ErroneousIoStatementState
>(
430 IostatBadOpOnChildUnit
, nullptr /* no unit */, sourceFile
,
433 return &unit
->BeginIoStatement
<ExternalMiscIoStatementState
>(terminator
,
434 *unit
, ExternalMiscIoStatementState::Rewind
, sourceFile
, sourceLine
);
441 Cookie
IODEF(BeginInquireUnit
)(
442 ExternalUnit unitNumber
, const char *sourceFile
, int sourceLine
) {
443 Terminator terminator
{sourceFile
, sourceLine
};
444 if (ExternalFileUnit
* unit
{ExternalFileUnit::LookUp(unitNumber
)}) {
445 if (ChildIo
* child
{unit
->GetChildIo()}) {
446 return &child
->BeginIoStatement
<InquireUnitState
>(
447 *unit
, sourceFile
, sourceLine
);
449 return &unit
->BeginIoStatement
<InquireUnitState
>(
450 terminator
, *unit
, sourceFile
, sourceLine
);
453 // INQUIRE(UNIT=unrecognized unit)
454 return &New
<InquireNoUnitState
>{terminator
}(
455 sourceFile
, sourceLine
, unitNumber
)
457 ->ioStatementState();
461 Cookie
IODEF(BeginInquireFile
)(const char *path
, std::size_t pathLength
,
462 const char *sourceFile
, int sourceLine
) {
463 Terminator terminator
{sourceFile
, sourceLine
};
464 auto trimmed
{SaveDefaultCharacter(
465 path
, TrimTrailingSpaces(path
, pathLength
), terminator
)};
466 if (ExternalFileUnit
*
467 unit
{ExternalFileUnit::LookUp(
468 trimmed
.get(), Fortran::runtime::strlen(trimmed
.get()))}) {
469 // INQUIRE(FILE=) to a connected unit
470 if (ChildIo
* child
{unit
->GetChildIo()}) {
471 return &child
->BeginIoStatement
<InquireUnitState
>(
472 *unit
, sourceFile
, sourceLine
);
474 return &unit
->BeginIoStatement
<InquireUnitState
>(
475 terminator
, *unit
, sourceFile
, sourceLine
);
478 return &New
<InquireUnconnectedFileState
>{terminator
}(
479 std::move(trimmed
), sourceFile
, sourceLine
)
481 ->ioStatementState();
485 Cookie
IODEF(BeginInquireIoLength
)(const char *sourceFile
, int sourceLine
) {
486 Terminator oom
{sourceFile
, sourceLine
};
487 return &New
<InquireIOLengthState
>{oom
}(sourceFile
, sourceLine
)
489 ->ioStatementState();
492 // Control list items
494 void IODEF(EnableHandlers
)(Cookie cookie
, bool hasIoStat
, bool hasErr
,
495 bool hasEnd
, bool hasEor
, bool hasIoMsg
) {
496 IoErrorHandler
&handler
{cookie
->GetIoErrorHandler()};
501 handler
.HasErrLabel();
504 handler
.HasEndLabel();
507 handler
.HasEorLabel();
514 static RT_API_ATTRS
bool YesOrNo(const char *keyword
, std::size_t length
,
515 const char *what
, IoErrorHandler
&handler
) {
516 static const char *keywords
[]{"YES", "NO", nullptr};
517 switch (IdentifyValue(keyword
, length
, keywords
)) {
523 handler
.SignalError(IostatErrorInKeyword
, "Invalid %s='%.*s'", what
,
524 static_cast<int>(length
), keyword
);
529 bool IODEF(SetAdvance
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
530 IoStatementState
&io
{*cookie
};
531 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
532 bool nonAdvancing
{!YesOrNo(keyword
, length
, "ADVANCE", handler
)};
533 if (nonAdvancing
&& io
.GetConnectionState().access
== Access::Direct
) {
534 handler
.SignalError("Non-advancing I/O attempted on direct access file");
536 auto *unit
{io
.GetExternalFileUnit()};
537 if (unit
&& unit
->GetChildIo()) {
538 // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3)
540 io
.mutableModes().nonAdvancing
= nonAdvancing
;
543 return !handler
.InError();
546 bool IODEF(SetBlank
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
547 IoStatementState
&io
{*cookie
};
548 static const char *keywords
[]{"NULL", "ZERO", nullptr};
549 switch (IdentifyValue(keyword
, length
, keywords
)) {
551 io
.mutableModes().editingFlags
&= ~blankZero
;
554 io
.mutableModes().editingFlags
|= blankZero
;
557 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
558 "Invalid BLANK='%.*s'", static_cast<int>(length
), keyword
);
563 bool IODEF(SetDecimal
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
564 IoStatementState
&io
{*cookie
};
565 static const char *keywords
[]{"COMMA", "POINT", nullptr};
566 switch (IdentifyValue(keyword
, length
, keywords
)) {
568 io
.mutableModes().editingFlags
|= decimalComma
;
571 io
.mutableModes().editingFlags
&= ~decimalComma
;
574 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
575 "Invalid DECIMAL='%.*s'", static_cast<int>(length
), keyword
);
580 bool IODEF(SetDelim
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
581 IoStatementState
&io
{*cookie
};
582 static const char *keywords
[]{"APOSTROPHE", "QUOTE", "NONE", nullptr};
583 switch (IdentifyValue(keyword
, length
, keywords
)) {
585 io
.mutableModes().delim
= '\'';
588 io
.mutableModes().delim
= '"';
591 io
.mutableModes().delim
= '\0';
594 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
595 "Invalid DELIM='%.*s'", static_cast<int>(length
), keyword
);
600 bool IODEF(SetPad
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
601 IoStatementState
&io
{*cookie
};
602 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
603 io
.mutableModes().pad
= YesOrNo(keyword
, length
, "PAD", handler
);
604 return !handler
.InError();
607 bool IODEF(SetPos
)(Cookie cookie
, std::int64_t pos
) {
608 IoStatementState
&io
{*cookie
};
609 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
610 if (auto *unit
{io
.GetExternalFileUnit()}) {
611 return unit
->SetStreamPos(pos
, handler
);
612 } else if (!io
.get_if
<ErroneousIoStatementState
>()) {
613 handler
.Crash("SetPos() called on internal unit");
618 bool IODEF(SetRec
)(Cookie cookie
, std::int64_t rec
) {
619 IoStatementState
&io
{*cookie
};
620 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
621 if (auto *unit
{io
.GetExternalFileUnit()}) {
622 if (unit
->GetChildIo()) {
624 IostatBadOpOnChildUnit
, "REC= specifier on child I/O");
626 unit
->SetDirectRec(rec
, handler
);
628 } else if (!io
.get_if
<ErroneousIoStatementState
>()) {
629 handler
.Crash("SetRec() called on internal unit");
634 bool IODEF(SetRound
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
635 IoStatementState
&io
{*cookie
};
636 static const char *keywords
[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE",
637 "PROCESSOR_DEFINED", nullptr};
638 switch (IdentifyValue(keyword
, length
, keywords
)) {
640 io
.mutableModes().round
= decimal::RoundUp
;
643 io
.mutableModes().round
= decimal::RoundDown
;
646 io
.mutableModes().round
= decimal::RoundToZero
;
649 io
.mutableModes().round
= decimal::RoundNearest
;
652 io
.mutableModes().round
= decimal::RoundCompatible
;
655 io
.mutableModes().round
= executionEnvironment
.defaultOutputRoundingMode
;
658 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
659 "Invalid ROUND='%.*s'", static_cast<int>(length
), keyword
);
664 bool IODEF(SetSign
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
665 IoStatementState
&io
{*cookie
};
666 static const char *keywords
[]{
667 "PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr};
668 switch (IdentifyValue(keyword
, length
, keywords
)) {
670 io
.mutableModes().editingFlags
|= signPlus
;
673 case 2: // processor default is SS
674 io
.mutableModes().editingFlags
&= ~signPlus
;
677 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
678 "Invalid SIGN='%.*s'", static_cast<int>(length
), keyword
);
683 bool IODEF(SetAccess
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
684 IoStatementState
&io
{*cookie
};
685 auto *open
{io
.get_if
<OpenStatementState
>()};
687 if (!io
.get_if
<NoopStatementState
>() &&
688 !io
.get_if
<ErroneousIoStatementState
>()) {
689 io
.GetIoErrorHandler().Crash(
690 "SetAccess() called when not in an OPEN statement");
693 } else if (open
->completedOperation()) {
694 io
.GetIoErrorHandler().Crash(
695 "SetAccess() called after GetNewUnit() for an OPEN statement");
697 static const char *keywords
[]{
698 "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr};
699 switch (IdentifyValue(keyword
, length
, keywords
)) {
701 open
->set_access(Access::Sequential
);
704 open
->set_access(Access::Direct
);
707 open
->set_access(Access::Stream
);
709 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND
710 open
->set_position(Position::Append
);
713 open
->SignalError(IostatErrorInKeyword
, "Invalid ACCESS='%.*s'",
714 static_cast<int>(length
), keyword
);
719 bool IODEF(SetAction
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
720 IoStatementState
&io
{*cookie
};
721 auto *open
{io
.get_if
<OpenStatementState
>()};
723 if (!io
.get_if
<NoopStatementState
>() &&
724 !io
.get_if
<ErroneousIoStatementState
>()) {
725 io
.GetIoErrorHandler().Crash(
726 "SetAction() called when not in an OPEN statement");
729 } else if (open
->completedOperation()) {
730 io
.GetIoErrorHandler().Crash(
731 "SetAction() called after GetNewUnit() for an OPEN statement");
733 Fortran::common::optional
<Action
> action
;
734 static const char *keywords
[]{"READ", "WRITE", "READWRITE", nullptr};
735 switch (IdentifyValue(keyword
, length
, keywords
)) {
737 action
= Action::Read
;
740 action
= Action::Write
;
743 action
= Action::ReadWrite
;
746 open
->SignalError(IostatErrorInKeyword
, "Invalid ACTION='%.*s'",
747 static_cast<int>(length
), keyword
);
750 RUNTIME_CHECK(io
.GetIoErrorHandler(), action
.has_value());
751 if (open
->wasExtant()) {
752 if ((*action
!= Action::Write
) != open
->unit().mayRead() ||
753 (*action
!= Action::Read
) != open
->unit().mayWrite()) {
754 open
->SignalError("ACTION= may not be changed on an open unit");
757 open
->set_action(*action
);
761 bool IODEF(SetAsynchronous
)(
762 Cookie cookie
, const char *keyword
, std::size_t length
) {
763 IoStatementState
&io
{*cookie
};
764 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
765 bool isYes
{YesOrNo(keyword
, length
, "ASYNCHRONOUS", handler
)};
766 if (auto *open
{io
.get_if
<OpenStatementState
>()}) {
767 if (open
->completedOperation()) {
769 "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
771 open
->unit().set_mayAsynchronous(isYes
);
772 } else if (auto *ext
{io
.get_if
<ExternalIoStatementBase
>()}) {
774 if (ext
->unit().mayAsynchronous()) {
775 ext
->SetAsynchronous();
777 handler
.SignalError(IostatBadAsynchronous
);
780 } else if (!io
.get_if
<NoopStatementState
>() &&
781 !io
.get_if
<ErroneousIoStatementState
>()) {
782 handler
.Crash("SetAsynchronous() called when not in an OPEN or external "
785 return !handler
.InError();
788 bool IODEF(SetCarriagecontrol
)(
789 Cookie cookie
, const char *keyword
, std::size_t length
) {
790 IoStatementState
&io
{*cookie
};
791 auto *open
{io
.get_if
<OpenStatementState
>()};
793 if (!io
.get_if
<NoopStatementState
>() &&
794 !io
.get_if
<ErroneousIoStatementState
>()) {
795 io
.GetIoErrorHandler().Crash(
796 "SetCarriageControl() called when not in an OPEN statement");
799 } else if (open
->completedOperation()) {
800 io
.GetIoErrorHandler().Crash(
801 "SetCarriageControl() called after GetNewUnit() for an OPEN statement");
803 static const char *keywords
[]{"LIST", "FORTRAN", "NONE", nullptr};
804 switch (IdentifyValue(keyword
, length
, keywords
)) {
809 open
->SignalError(IostatErrorInKeyword
,
810 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length
),
814 open
->SignalError(IostatErrorInKeyword
, "Invalid CARRIAGECONTROL='%.*s'",
815 static_cast<int>(length
), keyword
);
820 bool IODEF(SetConvert
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
821 IoStatementState
&io
{*cookie
};
822 auto *open
{io
.get_if
<OpenStatementState
>()};
824 if (!io
.get_if
<NoopStatementState
>() &&
825 !io
.get_if
<ErroneousIoStatementState
>()) {
826 io
.GetIoErrorHandler().Crash(
827 "SetConvert() called when not in an OPEN statement");
830 } else if (open
->completedOperation()) {
831 io
.GetIoErrorHandler().Crash(
832 "SetConvert() called after GetNewUnit() for an OPEN statement");
834 if (auto convert
{GetConvertFromString(keyword
, length
)}) {
835 open
->set_convert(*convert
);
838 open
->SignalError(IostatErrorInKeyword
, "Invalid CONVERT='%.*s'",
839 static_cast<int>(length
), keyword
);
844 bool IODEF(SetEncoding
)(
845 Cookie cookie
, const char *keyword
, std::size_t length
) {
846 IoStatementState
&io
{*cookie
};
847 auto *open
{io
.get_if
<OpenStatementState
>()};
849 if (!io
.get_if
<NoopStatementState
>() &&
850 !io
.get_if
<ErroneousIoStatementState
>()) {
851 io
.GetIoErrorHandler().Crash(
852 "SetEncoding() called when not in an OPEN statement");
855 } else if (open
->completedOperation()) {
856 io
.GetIoErrorHandler().Crash(
857 "SetEncoding() called after GetNewUnit() for an OPEN statement");
859 // Allow the encoding to be changed on an open unit -- it's
861 static const char *keywords
[]{"UTF-8", "DEFAULT", nullptr};
862 switch (IdentifyValue(keyword
, length
, keywords
)) {
864 open
->unit().isUTF8
= true;
867 open
->unit().isUTF8
= false;
870 open
->SignalError(IostatErrorInKeyword
, "Invalid ENCODING='%.*s'",
871 static_cast<int>(length
), keyword
);
876 bool IODEF(SetForm
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
877 IoStatementState
&io
{*cookie
};
878 auto *open
{io
.get_if
<OpenStatementState
>()};
880 if (!io
.get_if
<NoopStatementState
>() &&
881 !io
.get_if
<ErroneousIoStatementState
>()) {
882 io
.GetIoErrorHandler().Crash(
883 "SetForm() called when not in an OPEN statement");
885 } else if (open
->completedOperation()) {
886 io
.GetIoErrorHandler().Crash(
887 "SetForm() called after GetNewUnit() for an OPEN statement");
889 static const char *keywords
[]{"FORMATTED", "UNFORMATTED", nullptr};
890 switch (IdentifyValue(keyword
, length
, keywords
)) {
892 open
->set_isUnformatted(false);
895 open
->set_isUnformatted(true);
898 open
->SignalError(IostatErrorInKeyword
, "Invalid FORM='%.*s'",
899 static_cast<int>(length
), keyword
);
904 bool IODEF(SetPosition
)(
905 Cookie cookie
, const char *keyword
, std::size_t length
) {
906 IoStatementState
&io
{*cookie
};
907 auto *open
{io
.get_if
<OpenStatementState
>()};
909 if (!io
.get_if
<NoopStatementState
>() &&
910 !io
.get_if
<ErroneousIoStatementState
>()) {
911 io
.GetIoErrorHandler().Crash(
912 "SetPosition() called when not in an OPEN statement");
915 } else if (open
->completedOperation()) {
916 io
.GetIoErrorHandler().Crash(
917 "SetPosition() called after GetNewUnit() for an OPEN statement");
919 static const char *positions
[]{"ASIS", "REWIND", "APPEND", nullptr};
920 switch (IdentifyValue(keyword
, length
, positions
)) {
922 open
->set_position(Position::AsIs
);
925 open
->set_position(Position::Rewind
);
928 open
->set_position(Position::Append
);
931 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
932 "Invalid POSITION='%.*s'", static_cast<int>(length
), keyword
);
937 bool IODEF(SetRecl
)(Cookie cookie
, std::size_t n
) {
938 IoStatementState
&io
{*cookie
};
939 auto *open
{io
.get_if
<OpenStatementState
>()};
941 if (!io
.get_if
<NoopStatementState
>() &&
942 !io
.get_if
<ErroneousIoStatementState
>()) {
943 io
.GetIoErrorHandler().Crash(
944 "SetRecl() called when not in an OPEN statement");
947 } else if (open
->completedOperation()) {
948 io
.GetIoErrorHandler().Crash(
949 "SetRecl() called after GetNewUnit() for an OPEN statement");
951 if (static_cast<std::int64_t>(n
) <= 0) {
952 io
.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
954 } else if (open
->wasExtant() &&
955 open
->unit().openRecl
.value_or(0) != static_cast<std::int64_t>(n
)) {
956 open
->SignalError("RECL= may not be changed for an open unit");
959 open
->unit().openRecl
= n
;
964 bool IODEF(SetStatus
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
965 IoStatementState
&io
{*cookie
};
966 if (auto *open
{io
.get_if
<OpenStatementState
>()}) {
967 if (open
->completedOperation()) {
968 io
.GetIoErrorHandler().Crash(
969 "SetStatus() called after GetNewUnit() for an OPEN statement");
971 static const char *statuses
[]{
972 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr};
973 switch (IdentifyValue(keyword
, length
, statuses
)) {
975 open
->set_status(OpenStatus::Old
);
978 open
->set_status(OpenStatus::New
);
981 open
->set_status(OpenStatus::Scratch
);
984 open
->set_status(OpenStatus::Replace
);
987 open
->set_status(OpenStatus::Unknown
);
990 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
991 "Invalid STATUS='%.*s'", static_cast<int>(length
), keyword
);
995 if (auto *close
{io
.get_if
<CloseStatementState
>()}) {
996 static const char *statuses
[]{"KEEP", "DELETE", nullptr};
997 switch (IdentifyValue(keyword
, length
, statuses
)) {
999 close
->set_status(CloseStatus::Keep
);
1002 close
->set_status(CloseStatus::Delete
);
1005 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
1006 "Invalid STATUS='%.*s'", static_cast<int>(length
), keyword
);
1010 if (io
.get_if
<NoopStatementState
>() ||
1011 io
.get_if
<ErroneousIoStatementState
>()) {
1012 return true; // don't bother validating STATUS= in a no-op CLOSE
1014 io
.GetIoErrorHandler().Crash(
1015 "SetStatus() called when not in an OPEN or CLOSE statement");
1018 bool IODEF(SetFile
)(Cookie cookie
, const char *path
, std::size_t chars
) {
1019 IoStatementState
&io
{*cookie
};
1020 if (auto *open
{io
.get_if
<OpenStatementState
>()}) {
1021 if (open
->completedOperation()) {
1022 io
.GetIoErrorHandler().Crash(
1023 "SetFile() called after GetNewUnit() for an OPEN statement");
1025 open
->set_path(path
, chars
);
1027 } else if (!io
.get_if
<NoopStatementState
>() &&
1028 !io
.get_if
<ErroneousIoStatementState
>()) {
1029 io
.GetIoErrorHandler().Crash(
1030 "SetFile() called when not in an OPEN statement");
1035 bool IODEF(GetNewUnit
)(Cookie cookie
, int &unit
, int kind
) {
1036 IoStatementState
&io
{*cookie
};
1037 auto *open
{io
.get_if
<OpenStatementState
>()};
1039 if (!io
.get_if
<NoopStatementState
>() &&
1040 !io
.get_if
<ErroneousIoStatementState
>()) {
1041 io
.GetIoErrorHandler().Crash(
1042 "GetNewUnit() called when not in an OPEN statement");
1045 } else if (!open
->InError()) {
1046 open
->CompleteOperation();
1048 if (open
->InError()) {
1049 // A failed OPEN(NEWUNIT=n) does not modify 'n'
1052 std::int64_t result
{open
->unit().unitNumber()};
1053 if (!SetInteger(unit
, kind
, result
)) {
1054 open
->SignalError("GetNewUnit(): bad INTEGER kind(%d) or out-of-range "
1055 "value(%jd) for result",
1056 kind
, static_cast<std::intmax_t>(result
));
1063 bool IODEF(OutputDescriptor
)(Cookie cookie
, const Descriptor
&descriptor
) {
1064 return descr::DescriptorIO
<Direction::Output
>(*cookie
, descriptor
);
1067 bool IODEF(InputDescriptor
)(Cookie cookie
, const Descriptor
&descriptor
) {
1068 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1071 bool IODEF(InputInteger
)(Cookie cookie
, std::int64_t &n
, int kind
) {
1072 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputInteger")) {
1075 StaticDescriptor
<0> staticDescriptor
;
1076 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1077 descriptor
.Establish(
1078 TypeCategory::Integer
, kind
, reinterpret_cast<void *>(&n
), 0);
1079 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1082 bool IODEF(InputReal32
)(Cookie cookie
, float &x
) {
1083 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputReal32")) {
1086 StaticDescriptor
<0> staticDescriptor
;
1087 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1088 descriptor
.Establish(TypeCategory::Real
, 4, reinterpret_cast<void *>(&x
), 0);
1089 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1092 bool IODEF(InputReal64
)(Cookie cookie
, double &x
) {
1093 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputReal64")) {
1096 StaticDescriptor
<0> staticDescriptor
;
1097 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1098 descriptor
.Establish(TypeCategory::Real
, 8, reinterpret_cast<void *>(&x
), 0);
1099 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1102 bool IODEF(InputComplex32
)(Cookie cookie
, float z
[2]) {
1103 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputComplex32")) {
1106 StaticDescriptor
<0> staticDescriptor
;
1107 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1108 descriptor
.Establish(
1109 TypeCategory::Complex
, 4, reinterpret_cast<void *>(z
), 0);
1110 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1113 bool IODEF(InputComplex64
)(Cookie cookie
, double z
[2]) {
1114 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputComplex64")) {
1117 StaticDescriptor
<0> staticDescriptor
;
1118 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1119 descriptor
.Establish(
1120 TypeCategory::Complex
, 8, reinterpret_cast<void *>(z
), 0);
1121 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1124 bool IODEF(OutputCharacter
)(
1125 Cookie cookie
, const char *x
, std::size_t length
, int kind
) {
1126 if (!cookie
->CheckFormattedStmtType
<Direction::Output
>("OutputCharacter")) {
1129 StaticDescriptor
<0> staticDescriptor
;
1130 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1131 descriptor
.Establish(
1132 kind
, length
, reinterpret_cast<void *>(const_cast<char *>(x
)), 0);
1133 return descr::DescriptorIO
<Direction::Output
>(*cookie
, descriptor
);
1136 bool IODEF(InputCharacter
)(
1137 Cookie cookie
, char *x
, std::size_t length
, int kind
) {
1138 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputCharacter")) {
1141 StaticDescriptor
<0> staticDescriptor
;
1142 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1143 descriptor
.Establish(kind
, length
, reinterpret_cast<void *>(x
), 0);
1144 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1147 bool IODEF(InputAscii
)(Cookie cookie
, char *x
, std::size_t length
) {
1148 return IONAME(InputCharacter
)(cookie
, x
, length
, 1);
1151 bool IODEF(InputLogical
)(Cookie cookie
, bool &truth
) {
1152 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputLogical")) {
1155 StaticDescriptor
<0> staticDescriptor
;
1156 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1157 descriptor
.Establish(
1158 TypeCategory::Logical
, sizeof truth
, reinterpret_cast<void *>(&truth
), 0);
1159 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1162 bool IODEF(OutputDerivedType
)(Cookie cookie
, const Descriptor
&descriptor
,
1163 const NonTbpDefinedIoTable
*table
) {
1164 return descr::DescriptorIO
<Direction::Output
>(*cookie
, descriptor
, table
);
1167 bool IODEF(InputDerivedType
)(Cookie cookie
, const Descriptor
&descriptor
,
1168 const NonTbpDefinedIoTable
*table
) {
1169 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
, table
);
1172 std::size_t IODEF(GetSize
)(Cookie cookie
) {
1173 IoStatementState
&io
{*cookie
};
1174 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1175 if (!handler
.InError()) {
1176 io
.CompleteOperation();
1178 if (const auto *formatted
{
1179 io
.get_if
<FormattedIoStatementState
<Direction::Input
>>()}) {
1180 return formatted
->GetEditDescriptorChars();
1181 } else if (!io
.get_if
<NoopStatementState
>() &&
1182 !io
.get_if
<ErroneousIoStatementState
>()) {
1183 handler
.Crash("GetIoSize() called for an I/O statement that is not a "
1184 "formatted READ()");
1189 std::size_t IODEF(GetIoLength
)(Cookie cookie
) {
1190 IoStatementState
&io
{*cookie
};
1191 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1192 if (!handler
.InError()) {
1193 io
.CompleteOperation();
1195 if (const auto *inq
{io
.get_if
<InquireIOLengthState
>()}) {
1196 return inq
->bytes();
1197 } else if (!io
.get_if
<NoopStatementState
>() &&
1198 !io
.get_if
<ErroneousIoStatementState
>()) {
1199 handler
.Crash("GetIoLength() called for an I/O statement that is not "
1200 "INQUIRE(IOLENGTH=)");
1205 void IODEF(GetIoMsg
)(Cookie cookie
, char *msg
, std::size_t length
) {
1206 IoStatementState
&io
{*cookie
};
1207 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1208 if (!handler
.InError()) {
1209 io
.CompleteOperation();
1211 if (handler
.InError()) { // leave "msg" alone when no error
1212 handler
.GetIoMsg(msg
, length
);
1216 AsynchronousId
IODEF(GetAsynchronousId
)(Cookie cookie
) {
1217 IoStatementState
&io
{*cookie
};
1218 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1219 if (auto *ext
{io
.get_if
<ExternalIoStatementBase
>()}) {
1220 return ext
->asynchronousID();
1221 } else if (!io
.get_if
<NoopStatementState
>() &&
1222 !io
.get_if
<ErroneousIoStatementState
>()) {
1224 "GetAsynchronousId() called when not in an external I/O statement");
1229 bool IODEF(InquireCharacter
)(Cookie cookie
, InquiryKeywordHash inquiry
,
1230 char *result
, std::size_t length
) {
1231 IoStatementState
&io
{*cookie
};
1232 return io
.Inquire(inquiry
, result
, length
);
1235 bool IODEF(InquireLogical
)(
1236 Cookie cookie
, InquiryKeywordHash inquiry
, bool &result
) {
1237 IoStatementState
&io
{*cookie
};
1238 return io
.Inquire(inquiry
, result
);
1241 bool IODEF(InquirePendingId
)(Cookie cookie
, AsynchronousId id
, bool &result
) {
1242 IoStatementState
&io
{*cookie
};
1243 return io
.Inquire(HashInquiryKeyword("PENDING"), id
, result
);
1246 bool IODEF(InquireInteger64
)(
1247 Cookie cookie
, InquiryKeywordHash inquiry
, std::int64_t &result
, int kind
) {
1248 IoStatementState
&io
{*cookie
};
1249 std::int64_t n
{0}; // safe "undefined" value
1250 if (io
.Inquire(inquiry
, n
)) {
1251 if (SetInteger(result
, kind
, n
)) {
1254 io
.GetIoErrorHandler().SignalError(
1255 "InquireInteger64(): bad INTEGER kind(%d) or out-of-range "
1256 "value(%jd) for result",
1257 kind
, static_cast<std::intmax_t>(n
));
1262 template <typename INT
>
1263 static RT_API_ATTRS
enum Iostat
CheckUnitNumberInRangeImpl(INT unit
,
1264 bool handleError
, char *ioMsg
, std::size_t ioMsgLength
,
1265 const char *sourceFile
, int sourceLine
) {
1266 static_assert(sizeof(INT
) >= sizeof(ExternalUnit
),
1267 "only intended to be used when the INT to ExternalUnit conversion is "
1269 if (unit
!= static_cast<ExternalUnit
>(unit
)) {
1270 Terminator oom
{sourceFile
, sourceLine
};
1271 IoErrorHandler errorHandler
{oom
};
1273 errorHandler
.HasIoStat();
1275 errorHandler
.HasIoMsg();
1278 // Only provide the bad unit number in the message if SignalError can print
1279 // it accurately. Otherwise, the generic IostatUnitOverflow message will be
1281 if constexpr (sizeof(INT
) > sizeof(std::intmax_t)) {
1282 errorHandler
.SignalError(IostatUnitOverflow
);
1283 } else if (static_cast<std::intmax_t>(unit
) == unit
) {
1284 errorHandler
.SignalError(IostatUnitOverflow
,
1285 "UNIT number %jd is out of range", static_cast<std::intmax_t>(unit
));
1287 errorHandler
.SignalError(IostatUnitOverflow
);
1290 errorHandler
.GetIoMsg(ioMsg
, ioMsgLength
);
1292 return static_cast<enum Iostat
>(errorHandler
.GetIoStat());
1297 enum Iostat
IODEF(CheckUnitNumberInRange64
)(std::int64_t unit
, bool handleError
,
1298 char *ioMsg
, std::size_t ioMsgLength
, const char *sourceFile
,
1300 return CheckUnitNumberInRangeImpl(
1301 unit
, handleError
, ioMsg
, ioMsgLength
, sourceFile
, sourceLine
);
1304 #ifdef __SIZEOF_INT128__
1305 enum Iostat
IODEF(CheckUnitNumberInRange128
)(common::int128_t unit
,
1306 bool handleError
, char *ioMsg
, std::size_t ioMsgLength
,
1307 const char *sourceFile
, int sourceLine
) {
1308 return CheckUnitNumberInRangeImpl(
1309 unit
, handleError
, ioMsg
, ioMsgLength
, sourceFile
, sourceLine
);
1313 RT_EXT_API_GROUP_END
1314 } // namespace Fortran::runtime::io