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");
627 unit
->SetDirectRec(rec
, handler
);
629 } else if (!io
.get_if
<ErroneousIoStatementState
>()) {
630 handler
.Crash("SetRec() called on internal unit");
635 bool IODEF(SetRound
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
636 IoStatementState
&io
{*cookie
};
637 static const char *keywords
[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE",
638 "PROCESSOR_DEFINED", nullptr};
639 switch (IdentifyValue(keyword
, length
, keywords
)) {
641 io
.mutableModes().round
= decimal::RoundUp
;
644 io
.mutableModes().round
= decimal::RoundDown
;
647 io
.mutableModes().round
= decimal::RoundToZero
;
650 io
.mutableModes().round
= decimal::RoundNearest
;
653 io
.mutableModes().round
= decimal::RoundCompatible
;
656 io
.mutableModes().round
= executionEnvironment
.defaultOutputRoundingMode
;
659 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
660 "Invalid ROUND='%.*s'", static_cast<int>(length
), keyword
);
665 bool IODEF(SetSign
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
666 IoStatementState
&io
{*cookie
};
667 static const char *keywords
[]{
668 "PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr};
669 switch (IdentifyValue(keyword
, length
, keywords
)) {
671 io
.mutableModes().editingFlags
|= signPlus
;
674 case 2: // processor default is SS
675 io
.mutableModes().editingFlags
&= ~signPlus
;
678 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
679 "Invalid SIGN='%.*s'", static_cast<int>(length
), keyword
);
684 bool IODEF(SetAccess
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
685 IoStatementState
&io
{*cookie
};
686 auto *open
{io
.get_if
<OpenStatementState
>()};
688 if (!io
.get_if
<NoopStatementState
>() &&
689 !io
.get_if
<ErroneousIoStatementState
>()) {
690 io
.GetIoErrorHandler().Crash(
691 "SetAccess() called when not in an OPEN statement");
694 } else if (open
->completedOperation()) {
695 io
.GetIoErrorHandler().Crash(
696 "SetAccess() called after GetNewUnit() for an OPEN statement");
698 static const char *keywords
[]{
699 "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr};
700 switch (IdentifyValue(keyword
, length
, keywords
)) {
702 open
->set_access(Access::Sequential
);
705 open
->set_access(Access::Direct
);
708 open
->set_access(Access::Stream
);
710 case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND
711 open
->set_position(Position::Append
);
714 open
->SignalError(IostatErrorInKeyword
, "Invalid ACCESS='%.*s'",
715 static_cast<int>(length
), keyword
);
720 bool IODEF(SetAction
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
721 IoStatementState
&io
{*cookie
};
722 auto *open
{io
.get_if
<OpenStatementState
>()};
724 if (!io
.get_if
<NoopStatementState
>() &&
725 !io
.get_if
<ErroneousIoStatementState
>()) {
726 io
.GetIoErrorHandler().Crash(
727 "SetAction() called when not in an OPEN statement");
730 } else if (open
->completedOperation()) {
731 io
.GetIoErrorHandler().Crash(
732 "SetAction() called after GetNewUnit() for an OPEN statement");
734 Fortran::common::optional
<Action
> action
;
735 static const char *keywords
[]{"READ", "WRITE", "READWRITE", nullptr};
736 switch (IdentifyValue(keyword
, length
, keywords
)) {
738 action
= Action::Read
;
741 action
= Action::Write
;
744 action
= Action::ReadWrite
;
747 open
->SignalError(IostatErrorInKeyword
, "Invalid ACTION='%.*s'",
748 static_cast<int>(length
), keyword
);
751 RUNTIME_CHECK(io
.GetIoErrorHandler(), action
.has_value());
752 if (open
->wasExtant()) {
753 if ((*action
!= Action::Write
) != open
->unit().mayRead() ||
754 (*action
!= Action::Read
) != open
->unit().mayWrite()) {
755 open
->SignalError("ACTION= may not be changed on an open unit");
758 open
->set_action(*action
);
762 bool IODEF(SetAsynchronous
)(
763 Cookie cookie
, const char *keyword
, std::size_t length
) {
764 IoStatementState
&io
{*cookie
};
765 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
766 bool isYes
{YesOrNo(keyword
, length
, "ASYNCHRONOUS", handler
)};
767 if (auto *open
{io
.get_if
<OpenStatementState
>()}) {
768 if (open
->completedOperation()) {
770 "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
772 open
->unit().set_mayAsynchronous(isYes
);
774 // ASYNCHRONOUS='NO' is the default, so this is a no-op
775 } else if (auto *ext
{io
.get_if
<ExternalIoStatementBase
>()}) {
776 if (ext
->unit().mayAsynchronous()) {
777 ext
->SetAsynchronous();
779 handler
.SignalError(IostatBadAsynchronous
);
781 } else if (!io
.get_if
<NoopStatementState
>() &&
782 !io
.get_if
<ErroneousIoStatementState
>()) {
783 handler
.Crash("SetAsynchronous('YES') called when not in an OPEN or "
784 "external I/O statement");
786 return !handler
.InError();
789 bool IODEF(SetCarriagecontrol
)(
790 Cookie cookie
, const char *keyword
, std::size_t length
) {
791 IoStatementState
&io
{*cookie
};
792 auto *open
{io
.get_if
<OpenStatementState
>()};
794 if (!io
.get_if
<NoopStatementState
>() &&
795 !io
.get_if
<ErroneousIoStatementState
>()) {
796 io
.GetIoErrorHandler().Crash(
797 "SetCarriageControl() called when not in an OPEN statement");
800 } else if (open
->completedOperation()) {
801 io
.GetIoErrorHandler().Crash(
802 "SetCarriageControl() called after GetNewUnit() for an OPEN statement");
804 static const char *keywords
[]{"LIST", "FORTRAN", "NONE", nullptr};
805 switch (IdentifyValue(keyword
, length
, keywords
)) {
810 open
->SignalError(IostatErrorInKeyword
,
811 "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length
),
815 open
->SignalError(IostatErrorInKeyword
, "Invalid CARRIAGECONTROL='%.*s'",
816 static_cast<int>(length
), keyword
);
821 bool IODEF(SetConvert
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
822 IoStatementState
&io
{*cookie
};
823 auto *open
{io
.get_if
<OpenStatementState
>()};
825 if (!io
.get_if
<NoopStatementState
>() &&
826 !io
.get_if
<ErroneousIoStatementState
>()) {
827 io
.GetIoErrorHandler().Crash(
828 "SetConvert() called when not in an OPEN statement");
831 } else if (open
->completedOperation()) {
832 io
.GetIoErrorHandler().Crash(
833 "SetConvert() called after GetNewUnit() for an OPEN statement");
835 if (auto convert
{GetConvertFromString(keyword
, length
)}) {
836 open
->set_convert(*convert
);
839 open
->SignalError(IostatErrorInKeyword
, "Invalid CONVERT='%.*s'",
840 static_cast<int>(length
), keyword
);
845 bool IODEF(SetEncoding
)(
846 Cookie cookie
, const char *keyword
, std::size_t length
) {
847 IoStatementState
&io
{*cookie
};
848 auto *open
{io
.get_if
<OpenStatementState
>()};
850 if (!io
.get_if
<NoopStatementState
>() &&
851 !io
.get_if
<ErroneousIoStatementState
>()) {
852 io
.GetIoErrorHandler().Crash(
853 "SetEncoding() called when not in an OPEN statement");
856 } else if (open
->completedOperation()) {
857 io
.GetIoErrorHandler().Crash(
858 "SetEncoding() called after GetNewUnit() for an OPEN statement");
860 // Allow the encoding to be changed on an open unit -- it's
862 static const char *keywords
[]{"UTF-8", "DEFAULT", nullptr};
863 switch (IdentifyValue(keyword
, length
, keywords
)) {
865 open
->unit().isUTF8
= true;
868 open
->unit().isUTF8
= false;
871 open
->SignalError(IostatErrorInKeyword
, "Invalid ENCODING='%.*s'",
872 static_cast<int>(length
), keyword
);
877 bool IODEF(SetForm
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
878 IoStatementState
&io
{*cookie
};
879 auto *open
{io
.get_if
<OpenStatementState
>()};
881 if (!io
.get_if
<NoopStatementState
>() &&
882 !io
.get_if
<ErroneousIoStatementState
>()) {
883 io
.GetIoErrorHandler().Crash(
884 "SetForm() called when not in an OPEN statement");
886 } else if (open
->completedOperation()) {
887 io
.GetIoErrorHandler().Crash(
888 "SetForm() called after GetNewUnit() for an OPEN statement");
890 static const char *keywords
[]{"FORMATTED", "UNFORMATTED", "BINARY", nullptr};
891 switch (IdentifyValue(keyword
, length
, keywords
)) {
893 open
->set_isUnformatted(false);
896 open
->set_isUnformatted(true);
898 case 2: // legacy FORM='BINARY' means an unformatted stream
899 open
->set_isUnformatted(true);
900 open
->set_access(Access::Stream
);
903 open
->SignalError(IostatErrorInKeyword
, "Invalid FORM='%.*s'",
904 static_cast<int>(length
), keyword
);
909 bool IODEF(SetPosition
)(
910 Cookie cookie
, const char *keyword
, std::size_t length
) {
911 IoStatementState
&io
{*cookie
};
912 auto *open
{io
.get_if
<OpenStatementState
>()};
914 if (!io
.get_if
<NoopStatementState
>() &&
915 !io
.get_if
<ErroneousIoStatementState
>()) {
916 io
.GetIoErrorHandler().Crash(
917 "SetPosition() called when not in an OPEN statement");
920 } else if (open
->completedOperation()) {
921 io
.GetIoErrorHandler().Crash(
922 "SetPosition() called after GetNewUnit() for an OPEN statement");
924 static const char *positions
[]{"ASIS", "REWIND", "APPEND", nullptr};
925 switch (IdentifyValue(keyword
, length
, positions
)) {
927 open
->set_position(Position::AsIs
);
930 open
->set_position(Position::Rewind
);
933 open
->set_position(Position::Append
);
936 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
937 "Invalid POSITION='%.*s'", static_cast<int>(length
), keyword
);
942 bool IODEF(SetRecl
)(Cookie cookie
, std::size_t n
) {
943 IoStatementState
&io
{*cookie
};
944 auto *open
{io
.get_if
<OpenStatementState
>()};
946 if (!io
.get_if
<NoopStatementState
>() &&
947 !io
.get_if
<ErroneousIoStatementState
>()) {
948 io
.GetIoErrorHandler().Crash(
949 "SetRecl() called when not in an OPEN statement");
952 } else if (open
->completedOperation()) {
953 io
.GetIoErrorHandler().Crash(
954 "SetRecl() called after GetNewUnit() for an OPEN statement");
956 if (static_cast<std::int64_t>(n
) <= 0) {
957 io
.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
959 } else if (open
->wasExtant() &&
960 open
->unit().openRecl
.value_or(0) != static_cast<std::int64_t>(n
)) {
961 open
->SignalError("RECL= may not be changed for an open unit");
964 open
->unit().openRecl
= n
;
969 bool IODEF(SetStatus
)(Cookie cookie
, const char *keyword
, std::size_t length
) {
970 IoStatementState
&io
{*cookie
};
971 if (auto *open
{io
.get_if
<OpenStatementState
>()}) {
972 if (open
->completedOperation()) {
973 io
.GetIoErrorHandler().Crash(
974 "SetStatus() called after GetNewUnit() for an OPEN statement");
976 static const char *statuses
[]{
977 "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr};
978 switch (IdentifyValue(keyword
, length
, statuses
)) {
980 open
->set_status(OpenStatus::Old
);
983 open
->set_status(OpenStatus::New
);
986 open
->set_status(OpenStatus::Scratch
);
989 open
->set_status(OpenStatus::Replace
);
992 open
->set_status(OpenStatus::Unknown
);
995 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
996 "Invalid STATUS='%.*s'", static_cast<int>(length
), keyword
);
1000 if (auto *close
{io
.get_if
<CloseStatementState
>()}) {
1001 static const char *statuses
[]{"KEEP", "DELETE", nullptr};
1002 switch (IdentifyValue(keyword
, length
, statuses
)) {
1004 close
->set_status(CloseStatus::Keep
);
1007 close
->set_status(CloseStatus::Delete
);
1010 io
.GetIoErrorHandler().SignalError(IostatErrorInKeyword
,
1011 "Invalid STATUS='%.*s'", static_cast<int>(length
), keyword
);
1015 if (io
.get_if
<NoopStatementState
>() ||
1016 io
.get_if
<ErroneousIoStatementState
>()) {
1017 return true; // don't bother validating STATUS= in a no-op CLOSE
1019 io
.GetIoErrorHandler().Crash(
1020 "SetStatus() called when not in an OPEN or CLOSE statement");
1023 bool IODEF(SetFile
)(Cookie cookie
, const char *path
, std::size_t chars
) {
1024 IoStatementState
&io
{*cookie
};
1025 if (auto *open
{io
.get_if
<OpenStatementState
>()}) {
1026 if (open
->completedOperation()) {
1027 io
.GetIoErrorHandler().Crash(
1028 "SetFile() called after GetNewUnit() for an OPEN statement");
1030 open
->set_path(path
, chars
);
1032 } else if (!io
.get_if
<NoopStatementState
>() &&
1033 !io
.get_if
<ErroneousIoStatementState
>()) {
1034 io
.GetIoErrorHandler().Crash(
1035 "SetFile() called when not in an OPEN statement");
1040 bool IODEF(GetNewUnit
)(Cookie cookie
, int &unit
, int kind
) {
1041 IoStatementState
&io
{*cookie
};
1042 auto *open
{io
.get_if
<OpenStatementState
>()};
1044 if (!io
.get_if
<NoopStatementState
>() &&
1045 !io
.get_if
<ErroneousIoStatementState
>()) {
1046 io
.GetIoErrorHandler().Crash(
1047 "GetNewUnit() called when not in an OPEN statement");
1050 } else if (!open
->InError()) {
1051 open
->CompleteOperation();
1053 if (open
->InError()) {
1054 // A failed OPEN(NEWUNIT=n) does not modify 'n'
1057 std::int64_t result
{open
->unit().unitNumber()};
1058 if (!SetInteger(unit
, kind
, result
)) {
1059 open
->SignalError("GetNewUnit(): bad INTEGER kind(%d) or out-of-range "
1060 "value(%jd) for result",
1061 kind
, static_cast<std::intmax_t>(result
));
1068 bool IODEF(OutputDescriptor
)(Cookie cookie
, const Descriptor
&descriptor
) {
1069 return descr::DescriptorIO
<Direction::Output
>(*cookie
, descriptor
);
1072 bool IODEF(InputDescriptor
)(Cookie cookie
, const Descriptor
&descriptor
) {
1073 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1076 bool IODEF(InputInteger
)(Cookie cookie
, std::int64_t &n
, int kind
) {
1077 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputInteger")) {
1080 StaticDescriptor
<0> staticDescriptor
;
1081 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1082 descriptor
.Establish(
1083 TypeCategory::Integer
, kind
, reinterpret_cast<void *>(&n
), 0);
1084 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1087 bool IODEF(InputReal32
)(Cookie cookie
, float &x
) {
1088 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputReal32")) {
1091 StaticDescriptor
<0> staticDescriptor
;
1092 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1093 descriptor
.Establish(TypeCategory::Real
, 4, reinterpret_cast<void *>(&x
), 0);
1094 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1097 bool IODEF(InputReal64
)(Cookie cookie
, double &x
) {
1098 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputReal64")) {
1101 StaticDescriptor
<0> staticDescriptor
;
1102 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1103 descriptor
.Establish(TypeCategory::Real
, 8, reinterpret_cast<void *>(&x
), 0);
1104 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1107 bool IODEF(InputComplex32
)(Cookie cookie
, float z
[2]) {
1108 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputComplex32")) {
1111 StaticDescriptor
<0> staticDescriptor
;
1112 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1113 descriptor
.Establish(
1114 TypeCategory::Complex
, 4, reinterpret_cast<void *>(z
), 0);
1115 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1118 bool IODEF(InputComplex64
)(Cookie cookie
, double z
[2]) {
1119 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputComplex64")) {
1122 StaticDescriptor
<0> staticDescriptor
;
1123 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1124 descriptor
.Establish(
1125 TypeCategory::Complex
, 8, reinterpret_cast<void *>(z
), 0);
1126 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1129 bool IODEF(OutputCharacter
)(
1130 Cookie cookie
, const char *x
, std::size_t length
, int kind
) {
1131 if (!cookie
->CheckFormattedStmtType
<Direction::Output
>("OutputCharacter")) {
1134 StaticDescriptor
<0> staticDescriptor
;
1135 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1136 descriptor
.Establish(
1137 kind
, length
, reinterpret_cast<void *>(const_cast<char *>(x
)), 0);
1138 return descr::DescriptorIO
<Direction::Output
>(*cookie
, descriptor
);
1141 bool IODEF(InputCharacter
)(
1142 Cookie cookie
, char *x
, std::size_t length
, int kind
) {
1143 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputCharacter")) {
1146 StaticDescriptor
<0> staticDescriptor
;
1147 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1148 descriptor
.Establish(kind
, length
, reinterpret_cast<void *>(x
), 0);
1149 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1152 bool IODEF(InputAscii
)(Cookie cookie
, char *x
, std::size_t length
) {
1153 return IONAME(InputCharacter
)(cookie
, x
, length
, 1);
1156 bool IODEF(InputLogical
)(Cookie cookie
, bool &truth
) {
1157 if (!cookie
->CheckFormattedStmtType
<Direction::Input
>("InputLogical")) {
1160 StaticDescriptor
<0> staticDescriptor
;
1161 Descriptor
&descriptor
{staticDescriptor
.descriptor()};
1162 descriptor
.Establish(
1163 TypeCategory::Logical
, sizeof truth
, reinterpret_cast<void *>(&truth
), 0);
1164 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
);
1167 bool IODEF(OutputDerivedType
)(Cookie cookie
, const Descriptor
&descriptor
,
1168 const NonTbpDefinedIoTable
*table
) {
1169 return descr::DescriptorIO
<Direction::Output
>(*cookie
, descriptor
, table
);
1172 bool IODEF(InputDerivedType
)(Cookie cookie
, const Descriptor
&descriptor
,
1173 const NonTbpDefinedIoTable
*table
) {
1174 return descr::DescriptorIO
<Direction::Input
>(*cookie
, descriptor
, table
);
1177 std::size_t IODEF(GetSize
)(Cookie cookie
) {
1178 IoStatementState
&io
{*cookie
};
1179 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1180 if (!handler
.InError()) {
1181 io
.CompleteOperation();
1183 if (const auto *formatted
{
1184 io
.get_if
<FormattedIoStatementState
<Direction::Input
>>()}) {
1185 return formatted
->GetEditDescriptorChars();
1186 } else if (!io
.get_if
<NoopStatementState
>() &&
1187 !io
.get_if
<ErroneousIoStatementState
>()) {
1188 handler
.Crash("GetIoSize() called for an I/O statement that is not a "
1189 "formatted READ()");
1194 std::size_t IODEF(GetIoLength
)(Cookie cookie
) {
1195 IoStatementState
&io
{*cookie
};
1196 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1197 if (!handler
.InError()) {
1198 io
.CompleteOperation();
1200 if (const auto *inq
{io
.get_if
<InquireIOLengthState
>()}) {
1201 return inq
->bytes();
1202 } else if (!io
.get_if
<NoopStatementState
>() &&
1203 !io
.get_if
<ErroneousIoStatementState
>()) {
1204 handler
.Crash("GetIoLength() called for an I/O statement that is not "
1205 "INQUIRE(IOLENGTH=)");
1210 void IODEF(GetIoMsg
)(Cookie cookie
, char *msg
, std::size_t length
) {
1211 IoStatementState
&io
{*cookie
};
1212 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1213 if (!handler
.InError()) {
1214 io
.CompleteOperation();
1216 if (handler
.InError()) { // leave "msg" alone when no error
1217 handler
.GetIoMsg(msg
, length
);
1221 AsynchronousId
IODEF(GetAsynchronousId
)(Cookie cookie
) {
1222 IoStatementState
&io
{*cookie
};
1223 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
1224 if (auto *ext
{io
.get_if
<ExternalIoStatementBase
>()}) {
1225 return ext
->asynchronousID();
1226 } else if (!io
.get_if
<NoopStatementState
>() &&
1227 !io
.get_if
<ErroneousIoStatementState
>()) {
1229 "GetAsynchronousId() called when not in an external I/O statement");
1234 bool IODEF(InquireCharacter
)(Cookie cookie
, InquiryKeywordHash inquiry
,
1235 char *result
, std::size_t length
) {
1236 IoStatementState
&io
{*cookie
};
1237 return io
.Inquire(inquiry
, result
, length
);
1240 bool IODEF(InquireLogical
)(
1241 Cookie cookie
, InquiryKeywordHash inquiry
, bool &result
) {
1242 IoStatementState
&io
{*cookie
};
1243 return io
.Inquire(inquiry
, result
);
1246 bool IODEF(InquirePendingId
)(Cookie cookie
, AsynchronousId id
, bool &result
) {
1247 IoStatementState
&io
{*cookie
};
1248 return io
.Inquire(HashInquiryKeyword("PENDING"), id
, result
);
1251 bool IODEF(InquireInteger64
)(
1252 Cookie cookie
, InquiryKeywordHash inquiry
, std::int64_t &result
, int kind
) {
1253 IoStatementState
&io
{*cookie
};
1254 std::int64_t n
{0}; // safe "undefined" value
1255 if (io
.Inquire(inquiry
, n
)) {
1256 if (SetInteger(result
, kind
, n
)) {
1259 io
.GetIoErrorHandler().SignalError(
1260 "InquireInteger64(): bad INTEGER kind(%d) or out-of-range "
1261 "value(%jd) for result",
1262 kind
, static_cast<std::intmax_t>(n
));
1267 template <typename INT
>
1268 static RT_API_ATTRS
enum Iostat
CheckUnitNumberInRangeImpl(INT unit
,
1269 bool handleError
, char *ioMsg
, std::size_t ioMsgLength
,
1270 const char *sourceFile
, int sourceLine
) {
1271 static_assert(sizeof(INT
) >= sizeof(ExternalUnit
),
1272 "only intended to be used when the INT to ExternalUnit conversion is "
1274 if (unit
!= static_cast<ExternalUnit
>(unit
)) {
1275 Terminator oom
{sourceFile
, sourceLine
};
1276 IoErrorHandler errorHandler
{oom
};
1278 errorHandler
.HasIoStat();
1280 errorHandler
.HasIoMsg();
1283 // Only provide the bad unit number in the message if SignalError can print
1284 // it accurately. Otherwise, the generic IostatUnitOverflow message will be
1286 if constexpr (sizeof(INT
) > sizeof(std::intmax_t)) {
1287 errorHandler
.SignalError(IostatUnitOverflow
);
1288 } else if (static_cast<std::intmax_t>(unit
) == unit
) {
1289 errorHandler
.SignalError(IostatUnitOverflow
,
1290 "UNIT number %jd is out of range", static_cast<std::intmax_t>(unit
));
1292 errorHandler
.SignalError(IostatUnitOverflow
);
1295 errorHandler
.GetIoMsg(ioMsg
, ioMsgLength
);
1297 return static_cast<enum Iostat
>(errorHandler
.GetIoStat());
1302 enum Iostat
IODEF(CheckUnitNumberInRange64
)(std::int64_t unit
, bool handleError
,
1303 char *ioMsg
, std::size_t ioMsgLength
, const char *sourceFile
,
1305 return CheckUnitNumberInRangeImpl(
1306 unit
, handleError
, ioMsg
, ioMsgLength
, sourceFile
, sourceLine
);
1309 #ifdef __SIZEOF_INT128__
1310 enum Iostat
IODEF(CheckUnitNumberInRange128
)(common::int128_t unit
,
1311 bool handleError
, char *ioMsg
, std::size_t ioMsgLength
,
1312 const char *sourceFile
, int sourceLine
) {
1313 return CheckUnitNumberInRangeImpl(
1314 unit
, handleError
, ioMsg
, ioMsgLength
, sourceFile
, sourceLine
);
1318 RT_EXT_API_GROUP_END
1319 } // namespace Fortran::runtime::io