xref: /llvm-project/flang/runtime/io-api.cpp (revision fee393e4ea2b53139ee7924e3aa818433d70cfc7)
1651f58bfSDiana Picus //===-- runtime/io-api.cpp ------------------------------------------------===//
2352d347aSAlexis Perry //
3352d347aSAlexis Perry // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4352d347aSAlexis Perry // See https://llvm.org/LICENSE.txt for license information.
5352d347aSAlexis Perry // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6352d347aSAlexis Perry //
7352d347aSAlexis Perry //===----------------------------------------------------------------------===//
8352d347aSAlexis Perry 
9f7be2518Speter klausler // Implements the I/O statement API
10f7be2518Speter klausler 
11fe2ff545SPeter Klausler // template function BeginExternalListIo<> is in runtime/io-api-common.h.
12fe2ff545SPeter Klausler // APIs BeginExternalListOutput, OutputInteger{8,16,32,64,128},
13fe2ff545SPeter Klausler // OutputReal{32,64}, OutputComplex{32,64}, OutputAscii, & EndIoStatement()
14fe2ff545SPeter Klausler // are in runtime/io-api-minimal.cpp.
15fe2ff545SPeter Klausler 
16830c0b90SPeter Klausler #include "flang/Runtime/io-api.h"
17cc01194cSpeter klausler #include "descriptor-io.h"
183b635714Speter klausler #include "edit-input.h"
193b635714Speter klausler #include "edit-output.h"
2095696d56Speter klausler #include "environment.h"
21352d347aSAlexis Perry #include "format.h"
22fe2ff545SPeter Klausler #include "io-api-common.h"
23352d347aSAlexis Perry #include "io-stmt.h"
24352d347aSAlexis Perry #include "terminator.h"
2595696d56Speter klausler #include "tools.h"
26f7be2518Speter klausler #include "unit.h"
2771e0261fSSlava Zakharin #include "flang/Common/optional.h"
28830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h"
29830c0b90SPeter Klausler #include "flang/Runtime/memory.h"
30352d347aSAlexis Perry #include <cstdlib>
31352d347aSAlexis Perry #include <memory>
32352d347aSAlexis Perry 
33352d347aSAlexis Perry namespace Fortran::runtime::io {
34f3c31d70SSlava Zakharin RT_EXT_API_GROUP_BEGIN
35352d347aSAlexis Perry 
36f3c31d70SSlava Zakharin RT_API_ATTRS const char *InquiryKeywordHashDecode(
37675ad1bcSpeter klausler     char *buffer, std::size_t n, InquiryKeywordHash hash) {
38675ad1bcSpeter klausler   if (n < 1) {
39675ad1bcSpeter klausler     return nullptr;
40675ad1bcSpeter klausler   }
41675ad1bcSpeter klausler   char *p{buffer + n};
42675ad1bcSpeter klausler   *--p = '\0';
43675ad1bcSpeter klausler   while (hash > 1) {
44675ad1bcSpeter klausler     if (p < buffer) {
45675ad1bcSpeter klausler       return nullptr;
46675ad1bcSpeter klausler     }
47675ad1bcSpeter klausler     *--p = 'A' + (hash % 26);
48675ad1bcSpeter klausler     hash /= 26;
49675ad1bcSpeter klausler   }
50675ad1bcSpeter klausler   return hash == 1 ? p : nullptr;
51675ad1bcSpeter klausler }
52675ad1bcSpeter klausler 
533b635714Speter klausler template <Direction DIR>
54f3c31d70SSlava Zakharin RT_API_ATTRS Cookie BeginInternalArrayListIO(const Descriptor &descriptor,
5595696d56Speter klausler     void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
5695696d56Speter klausler     const char *sourceFile, int sourceLine) {
5795696d56Speter klausler   Terminator oom{sourceFile, sourceLine};
5898d576c7Speter klausler   return &New<InternalListIoStatementState<DIR>>{oom}(
5998d576c7Speter klausler       descriptor, sourceFile, sourceLine)
6098d576c7Speter klausler               .release()
6198d576c7Speter klausler               ->ioStatementState();
6295696d56Speter klausler }
6395696d56Speter klausler 
64f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalArrayListOutput)(const Descriptor &descriptor,
653b635714Speter klausler     void **scratchArea, std::size_t scratchBytes, const char *sourceFile,
663b635714Speter klausler     int sourceLine) {
673b635714Speter klausler   return BeginInternalArrayListIO<Direction::Output>(
683b635714Speter klausler       descriptor, scratchArea, scratchBytes, sourceFile, sourceLine);
693b635714Speter klausler }
703b635714Speter klausler 
71f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalArrayListInput)(const Descriptor &descriptor,
723b635714Speter klausler     void **scratchArea, std::size_t scratchBytes, const char *sourceFile,
733b635714Speter klausler     int sourceLine) {
743b635714Speter klausler   return BeginInternalArrayListIO<Direction::Input>(
753b635714Speter klausler       descriptor, scratchArea, scratchBytes, sourceFile, sourceLine);
763b635714Speter klausler }
773b635714Speter klausler 
783b635714Speter klausler template <Direction DIR>
79f3c31d70SSlava Zakharin RT_API_ATTRS Cookie BeginInternalArrayFormattedIO(const Descriptor &descriptor,
8027d666b9SV Donaldson     const char *format, std::size_t formatLength,
8127d666b9SV Donaldson     const Descriptor *formatDescriptor, void ** /*scratchArea*/,
8227d666b9SV Donaldson     std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
8395696d56Speter klausler   Terminator oom{sourceFile, sourceLine};
84cc180f4cSPeter Klausler   return &New<InternalFormattedIoStatementState<DIR>>{oom}(descriptor, format,
8527d666b9SV Donaldson       formatLength, formatDescriptor, sourceFile, sourceLine)
8698d576c7Speter klausler               .release()
8798d576c7Speter klausler               ->ioStatementState();
8895696d56Speter klausler }
8995696d56Speter klausler 
90f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor,
9127d666b9SV Donaldson     const char *format, std::size_t formatLength,
9227d666b9SV Donaldson     const Descriptor *formatDescriptor, void **scratchArea,
9327d666b9SV Donaldson     std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
943b635714Speter klausler   return BeginInternalArrayFormattedIO<Direction::Output>(descriptor, format,
9527d666b9SV Donaldson       formatLength, formatDescriptor, scratchArea, scratchBytes, sourceFile,
9627d666b9SV Donaldson       sourceLine);
973b635714Speter klausler }
983b635714Speter klausler 
99f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalArrayFormattedInput)(const Descriptor &descriptor,
10027d666b9SV Donaldson     const char *format, std::size_t formatLength,
10127d666b9SV Donaldson     const Descriptor *formatDescriptor, void **scratchArea,
10227d666b9SV Donaldson     std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
1033b635714Speter klausler   return BeginInternalArrayFormattedIO<Direction::Input>(descriptor, format,
10427d666b9SV Donaldson       formatLength, formatDescriptor, scratchArea, scratchBytes, sourceFile,
10527d666b9SV Donaldson       sourceLine);
1063b635714Speter klausler }
1073b635714Speter klausler 
1083b635714Speter klausler template <Direction DIR>
1098ebf7411SSlava Zakharin RT_API_ATTRS Cookie BeginInternalListIO(
110b30fa1c3Speter klausler     std::conditional_t<DIR == Direction::Input, const char, char> *internal,
111b30fa1c3Speter klausler     std::size_t internalLength, void ** /*scratchArea*/,
112b30fa1c3Speter klausler     std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
113b30fa1c3Speter klausler   Terminator oom{sourceFile, sourceLine};
114b30fa1c3Speter klausler   return &New<InternalListIoStatementState<DIR>>{oom}(
115b30fa1c3Speter klausler       internal, internalLength, sourceFile, sourceLine)
116b30fa1c3Speter klausler               .release()
117b30fa1c3Speter klausler               ->ioStatementState();
118b30fa1c3Speter klausler }
119b30fa1c3Speter klausler 
120f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalListOutput)(char *internal,
121b30fa1c3Speter klausler     std::size_t internalLength, void **scratchArea, std::size_t scratchBytes,
122b30fa1c3Speter klausler     const char *sourceFile, int sourceLine) {
123b30fa1c3Speter klausler   return BeginInternalListIO<Direction::Output>(internal, internalLength,
124b30fa1c3Speter klausler       scratchArea, scratchBytes, sourceFile, sourceLine);
125b30fa1c3Speter klausler }
126b30fa1c3Speter klausler 
127f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalListInput)(const char *internal,
128b30fa1c3Speter klausler     std::size_t internalLength, void **scratchArea, std::size_t scratchBytes,
129b30fa1c3Speter klausler     const char *sourceFile, int sourceLine) {
130b30fa1c3Speter klausler   return BeginInternalListIO<Direction::Input>(internal, internalLength,
131b30fa1c3Speter klausler       scratchArea, scratchBytes, sourceFile, sourceLine);
132b30fa1c3Speter klausler }
133b30fa1c3Speter klausler 
134b30fa1c3Speter klausler template <Direction DIR>
135f3c31d70SSlava Zakharin RT_API_ATTRS Cookie BeginInternalFormattedIO(
1363b635714Speter klausler     std::conditional_t<DIR == Direction::Input, const char, char> *internal,
1373b635714Speter klausler     std::size_t internalLength, const char *format, std::size_t formatLength,
13827d666b9SV Donaldson     const Descriptor *formatDescriptor, void ** /*scratchArea*/,
13927d666b9SV Donaldson     std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
14095696d56Speter klausler   Terminator oom{sourceFile, sourceLine};
141cc180f4cSPeter Klausler   return &New<InternalFormattedIoStatementState<DIR>>{oom}(internal,
14227d666b9SV Donaldson       internalLength, format, formatLength, formatDescriptor, sourceFile,
14327d666b9SV Donaldson       sourceLine)
14498d576c7Speter klausler               .release()
14598d576c7Speter klausler               ->ioStatementState();
14695696d56Speter klausler }
14795696d56Speter klausler 
148f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalFormattedOutput)(char *internal,
149352d347aSAlexis Perry     std::size_t internalLength, const char *format, std::size_t formatLength,
15027d666b9SV Donaldson     const Descriptor *formatDescriptor, void **scratchArea,
15127d666b9SV Donaldson     std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
1523b635714Speter klausler   return BeginInternalFormattedIO<Direction::Output>(internal, internalLength,
15327d666b9SV Donaldson       format, formatLength, formatDescriptor, scratchArea, scratchBytes,
15427d666b9SV Donaldson       sourceFile, sourceLine);
15595696d56Speter klausler }
15695696d56Speter klausler 
157f3c31d70SSlava Zakharin Cookie IODEF(BeginInternalFormattedInput)(const char *internal,
15895696d56Speter klausler     std::size_t internalLength, const char *format, std::size_t formatLength,
15927d666b9SV Donaldson     const Descriptor *formatDescriptor, void **scratchArea,
16027d666b9SV Donaldson     std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
1613b635714Speter klausler   return BeginInternalFormattedIO<Direction::Input>(internal, internalLength,
16227d666b9SV Donaldson       format, formatLength, formatDescriptor, scratchArea, scratchBytes,
16327d666b9SV Donaldson       sourceFile, sourceLine);
1643b635714Speter klausler }
1653b635714Speter klausler 
166f3c31d70SSlava Zakharin Cookie IODEF(BeginExternalListInput)(
1673b635714Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
1686a1c3efaSpeter klausler   return BeginExternalListIO<Direction::Input, ExternalListIoStatementState>(
169df38f35aSPeter Klausler       unitNumber, sourceFile, sourceLine);
1703b635714Speter klausler }
1713b635714Speter klausler 
1723b635714Speter klausler template <Direction DIR>
173f3c31d70SSlava Zakharin RT_API_ATTRS Cookie BeginExternalFormattedIO(const char *format,
174f3c31d70SSlava Zakharin     std::size_t formatLength, const Descriptor *formatDescriptor,
175f3c31d70SSlava Zakharin     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
1763b635714Speter klausler   Terminator terminator{sourceFile, sourceLine};
177cfbde714SPeter Klausler   Cookie errorCookie{nullptr};
178cfbde714SPeter Klausler   ExternalFileUnit *unit{GetOrCreateUnit(
179cfbde714SPeter Klausler       unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)};
180cfbde714SPeter Klausler   if (!unit) {
181cfbde714SPeter Klausler     return errorCookie;
182b1856009SPeter Klausler   }
183cfbde714SPeter Klausler   Iostat iostat{IostatOk};
184cfbde714SPeter Klausler   if (!unit->isUnformatted.has_value()) {
185cfbde714SPeter Klausler     unit->isUnformatted = false;
186cfbde714SPeter Klausler   }
187cfbde714SPeter Klausler   if (*unit->isUnformatted) {
188df38f35aSPeter Klausler     iostat = IostatFormattedIoOnUnformattedUnit;
189b1856009SPeter Klausler   }
190cfbde714SPeter Klausler   if (ChildIo * child{unit->GetChildIo()}) {
191df38f35aSPeter Klausler     if (iostat == IostatOk) {
192df38f35aSPeter Klausler       iostat = child->CheckFormattingAndDirection(false, DIR);
193df38f35aSPeter Klausler     }
194df38f35aSPeter Klausler     if (iostat == IostatOk) {
195df38f35aSPeter Klausler       return &child->BeginIoStatement<ChildFormattedIoStatementState<DIR>>(
19627d666b9SV Donaldson           *child, format, formatLength, formatDescriptor, sourceFile,
19727d666b9SV Donaldson           sourceLine);
19843fadefbSpeter klausler     } else {
199df38f35aSPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
20011f928afSPeter Klausler           iostat, nullptr /* no unit */, sourceFile, sourceLine);
201df38f35aSPeter Klausler     }
202df38f35aSPeter Klausler   } else {
203df38f35aSPeter Klausler     if (iostat == IostatOk) {
204cfbde714SPeter Klausler       iostat = unit->SetDirection(DIR);
205df38f35aSPeter Klausler     }
206df38f35aSPeter Klausler     if (iostat == IostatOk) {
207cfbde714SPeter Klausler       return &unit->BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
20827d666b9SV Donaldson           terminator, *unit, format, formatLength, formatDescriptor, sourceFile,
20927d666b9SV Donaldson           sourceLine);
210df38f35aSPeter Klausler     } else {
211cfbde714SPeter Klausler       return &unit->BeginIoStatement<ErroneousIoStatementState>(
212921316afSPeter Klausler           terminator, iostat, unit, sourceFile, sourceLine);
213df38f35aSPeter Klausler     }
214352d347aSAlexis Perry   }
21543fadefbSpeter klausler }
216352d347aSAlexis Perry 
217f3c31d70SSlava Zakharin Cookie IODEF(BeginExternalFormattedOutput)(const char *format,
21827d666b9SV Donaldson     std::size_t formatLength, const Descriptor *formatDescriptor,
21927d666b9SV Donaldson     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
220cc180f4cSPeter Klausler   return BeginExternalFormattedIO<Direction::Output>(format, formatLength,
22127d666b9SV Donaldson       formatDescriptor, unitNumber, sourceFile, sourceLine);
22295696d56Speter klausler }
22395696d56Speter klausler 
224f3c31d70SSlava Zakharin Cookie IODEF(BeginExternalFormattedInput)(const char *format,
22527d666b9SV Donaldson     std::size_t formatLength, const Descriptor *formatDescriptor,
22627d666b9SV Donaldson     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
227cc180f4cSPeter Klausler   return BeginExternalFormattedIO<Direction::Input>(format, formatLength,
22827d666b9SV Donaldson       formatDescriptor, unitNumber, sourceFile, sourceLine);
2293b635714Speter klausler }
2303b635714Speter klausler 
2313b635714Speter klausler template <Direction DIR>
232f3c31d70SSlava Zakharin RT_API_ATTRS Cookie BeginUnformattedIO(
23395696d56Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
23495696d56Speter klausler   Terminator terminator{sourceFile, sourceLine};
235cfbde714SPeter Klausler   Cookie errorCookie{nullptr};
236cfbde714SPeter Klausler   ExternalFileUnit *unit{GetOrCreateUnit(
237cfbde714SPeter Klausler       unitNumber, DIR, true /*unformatted*/, terminator, errorCookie)};
238cfbde714SPeter Klausler   if (!unit) {
239cfbde714SPeter Klausler     return errorCookie;
240b1856009SPeter Klausler   }
241cfbde714SPeter Klausler   Iostat iostat{IostatOk};
242cfbde714SPeter Klausler   if (!unit->isUnformatted.has_value()) {
243cfbde714SPeter Klausler     unit->isUnformatted = true;
244cfbde714SPeter Klausler   }
245cfbde714SPeter Klausler   if (!*unit->isUnformatted) {
246df38f35aSPeter Klausler     iostat = IostatUnformattedIoOnFormattedUnit;
247b1856009SPeter Klausler   }
248cfbde714SPeter Klausler   if (ChildIo * child{unit->GetChildIo()}) {
249df38f35aSPeter Klausler     if (iostat == IostatOk) {
250df38f35aSPeter Klausler       iostat = child->CheckFormattingAndDirection(true, DIR);
251df38f35aSPeter Klausler     }
252df38f35aSPeter Klausler     if (iostat == IostatOk) {
253df38f35aSPeter Klausler       return &child->BeginIoStatement<ChildUnformattedIoStatementState<DIR>>(
254df38f35aSPeter Klausler           *child, sourceFile, sourceLine);
25543fadefbSpeter klausler     } else {
256df38f35aSPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
25711f928afSPeter Klausler           iostat, nullptr /* no unit */, sourceFile, sourceLine);
258df38f35aSPeter Klausler     }
259df38f35aSPeter Klausler   } else {
260df38f35aSPeter Klausler     if (iostat == IostatOk) {
261cfbde714SPeter Klausler       iostat = unit->SetDirection(DIR);
262df38f35aSPeter Klausler     }
263df38f35aSPeter Klausler     if (iostat == IostatOk) {
26443fadefbSpeter klausler       IoStatementState &io{
265cfbde714SPeter Klausler           unit->BeginIoStatement<ExternalUnformattedIoStatementState<DIR>>(
266921316afSPeter Klausler               terminator, *unit, sourceFile, sourceLine)};
267d879ac8aSpeter klausler       if constexpr (DIR == Direction::Output) {
268cfbde714SPeter Klausler         if (unit->access == Access::Sequential) {
2693b635714Speter klausler           // Create space for (sub)record header to be completed by
270b03628d9Speter klausler           // ExternalFileUnit::AdvanceRecord()
271cfbde714SPeter Klausler           unit->recordLength.reset(); // in case of prior BACKSPACE
27295696d56Speter klausler           io.Emit("\0\0\0\0", 4); // placeholder for record length header
27395696d56Speter klausler         }
2743b635714Speter klausler       }
27595696d56Speter klausler       return &io;
276df38f35aSPeter Klausler     } else {
277cfbde714SPeter Klausler       return &unit->BeginIoStatement<ErroneousIoStatementState>(
278921316afSPeter Klausler           terminator, iostat, unit, sourceFile, sourceLine);
279df38f35aSPeter Klausler     }
28095696d56Speter klausler   }
28143fadefbSpeter klausler }
28295696d56Speter klausler 
283f3c31d70SSlava Zakharin Cookie IODEF(BeginUnformattedOutput)(
2843b635714Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
2853b635714Speter klausler   return BeginUnformattedIO<Direction::Output>(
2863b635714Speter klausler       unitNumber, sourceFile, sourceLine);
2873b635714Speter klausler }
2883b635714Speter klausler 
289f3c31d70SSlava Zakharin Cookie IODEF(BeginUnformattedInput)(
2903b635714Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
2913b635714Speter klausler   return BeginUnformattedIO<Direction::Input>(
2923b635714Speter klausler       unitNumber, sourceFile, sourceLine);
2933b635714Speter klausler }
2943b635714Speter klausler 
295f3c31d70SSlava Zakharin Cookie IODEF(BeginOpenUnit)( // OPEN(without NEWUNIT=)
29695696d56Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
2973b635714Speter klausler   Terminator terminator{sourceFile, sourceLine};
298cfbde714SPeter Klausler   bool wasExtant{false};
299cfbde714SPeter Klausler   if (ExternalFileUnit *
300cfbde714SPeter Klausler       unit{ExternalFileUnit::LookUpOrCreate(
301cfbde714SPeter Klausler           unitNumber, terminator, wasExtant)}) {
30279f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
30379f6b812SPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
30479f6b812SPeter Klausler           IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
30579f6b812SPeter Klausler           sourceLine);
30679f6b812SPeter Klausler     } else {
307afdbf1b7SPeter Klausler       return &unit->BeginIoStatement<OpenStatementState>(terminator, *unit,
308afdbf1b7SPeter Klausler           wasExtant, false /*not NEWUNIT=*/, sourceFile, sourceLine);
30979f6b812SPeter Klausler     }
310cfbde714SPeter Klausler   } else {
311c078e464SPeter Klausler     return NoopUnit(terminator, unitNumber, IostatBadUnitNumber);
312cfbde714SPeter Klausler   }
31395696d56Speter klausler }
31495696d56Speter klausler 
315f3c31d70SSlava Zakharin Cookie IODEF(BeginOpenNewUnit)( // OPEN(NEWUNIT=j)
31695696d56Speter klausler     const char *sourceFile, int sourceLine) {
3173b635714Speter klausler   Terminator terminator{sourceFile, sourceLine};
318c7f4c333SPeter Klausler   ExternalFileUnit &unit{
319c7f4c333SPeter Klausler       ExternalFileUnit::NewUnit(terminator, false /*not child I/O*/)};
320afdbf1b7SPeter Klausler   return &unit.BeginIoStatement<OpenStatementState>(terminator, unit,
321afdbf1b7SPeter Klausler       false /*was an existing file*/, true /*NEWUNIT=*/, sourceFile,
322afdbf1b7SPeter Klausler       sourceLine);
32395696d56Speter klausler }
32495696d56Speter klausler 
325f3c31d70SSlava Zakharin Cookie IODEF(BeginWait)(ExternalUnit unitNumber, AsynchronousId id,
326166d6ed5SPeter Klausler     const char *sourceFile, int sourceLine) {
327921316afSPeter Klausler   Terminator terminator{sourceFile, sourceLine};
328166d6ed5SPeter Klausler   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
329166d6ed5SPeter Klausler     if (unit->Wait(id)) {
330921316afSPeter Klausler       return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
331166d6ed5SPeter Klausler           *unit, ExternalMiscIoStatementState::Wait, sourceFile, sourceLine);
332166d6ed5SPeter Klausler     } else {
333166d6ed5SPeter Klausler       return &unit->BeginIoStatement<ErroneousIoStatementState>(
334921316afSPeter Klausler           terminator, IostatBadWaitId, unit, sourceFile, sourceLine);
335166d6ed5SPeter Klausler     }
336166d6ed5SPeter Klausler   } else {
337c078e464SPeter Klausler     return NoopUnit(
338c078e464SPeter Klausler         terminator, unitNumber, id == 0 ? IostatOk : IostatBadWaitUnit);
339deb62f5aSPeter Klausler   }
340166d6ed5SPeter Klausler }
341f3c31d70SSlava Zakharin Cookie IODEF(BeginWaitAll)(
342166d6ed5SPeter Klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
34317853928SPeter Klausler   return IONAME(BeginWait)(unitNumber, 0 /*no ID=*/, sourceFile, sourceLine);
344deb62f5aSPeter Klausler }
345deb62f5aSPeter Klausler 
346f3c31d70SSlava Zakharin Cookie IODEF(BeginClose)(
34795696d56Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
348921316afSPeter Klausler   Terminator terminator{sourceFile, sourceLine};
34979f6b812SPeter Klausler   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
35079f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
35179f6b812SPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
35279f6b812SPeter Klausler           IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
35379f6b812SPeter Klausler           sourceLine);
35479f6b812SPeter Klausler     }
35579f6b812SPeter Klausler   }
3563b635714Speter klausler   if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) {
35795696d56Speter klausler     return &unit->BeginIoStatement<CloseStatementState>(
358921316afSPeter Klausler         terminator, *unit, sourceFile, sourceLine);
35995696d56Speter klausler   } else {
36095696d56Speter klausler     // CLOSE(UNIT=bad unit) is just a no-op
361c078e464SPeter Klausler     return NoopUnit(terminator, unitNumber);
36295696d56Speter klausler   }
36395696d56Speter klausler }
36495696d56Speter klausler 
365f3c31d70SSlava Zakharin Cookie IODEF(BeginFlush)(
3665d5b9682Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
367921316afSPeter Klausler   Terminator terminator{sourceFile, sourceLine};
3689ddd0792SPeter Klausler   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
36979f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
37079f6b812SPeter Klausler       return &child->BeginIoStatement<ExternalMiscIoStatementState>(
37179f6b812SPeter Klausler           *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
37279f6b812SPeter Klausler     } else {
373921316afSPeter Klausler       return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
3749ddd0792SPeter Klausler           *unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
37579f6b812SPeter Klausler     }
3769ddd0792SPeter Klausler   } else {
377c078e464SPeter Klausler     // FLUSH(UNIT=bad unit) is an error; an unconnected unit is a no-op
378c078e464SPeter Klausler     return NoopUnit(terminator, unitNumber,
379c078e464SPeter Klausler         unitNumber >= 0 ? IostatOk : IostatBadFlushUnit);
3809ddd0792SPeter Klausler   }
3815d5b9682Speter klausler }
3825d5b9682Speter klausler 
383f3c31d70SSlava Zakharin Cookie IODEF(BeginBackspace)(
3845d5b9682Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
3855d5b9682Speter klausler   Terminator terminator{sourceFile, sourceLine};
386142db43bSPeter Klausler   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
38779f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
38879f6b812SPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
38979f6b812SPeter Klausler           IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
39079f6b812SPeter Klausler           sourceLine);
39179f6b812SPeter Klausler     } else {
392921316afSPeter Klausler       return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
39379f6b812SPeter Klausler           *unit, ExternalMiscIoStatementState::Backspace, sourceFile,
39479f6b812SPeter Klausler           sourceLine);
39579f6b812SPeter Klausler     }
396142db43bSPeter Klausler   } else {
397c078e464SPeter Klausler     return NoopUnit(terminator, unitNumber, IostatBadBackspaceUnit);
398142db43bSPeter Klausler   }
3995d5b9682Speter klausler }
4005d5b9682Speter klausler 
401f3c31d70SSlava Zakharin Cookie IODEF(BeginEndfile)(
4025d5b9682Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
4035d5b9682Speter klausler   Terminator terminator{sourceFile, sourceLine};
404cfbde714SPeter Klausler   Cookie errorCookie{nullptr};
405cfbde714SPeter Klausler   if (ExternalFileUnit *
40671e0261fSSlava Zakharin       unit{GetOrCreateUnit(unitNumber, Direction::Output,
40771e0261fSSlava Zakharin           Fortran::common::nullopt, terminator, errorCookie)}) {
40879f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
40979f6b812SPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
41079f6b812SPeter Klausler           IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
41179f6b812SPeter Klausler           sourceLine);
41279f6b812SPeter Klausler     } else {
413921316afSPeter Klausler       return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
414cfbde714SPeter Klausler           *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
41579f6b812SPeter Klausler     }
416cfbde714SPeter Klausler   } else {
417cfbde714SPeter Klausler     return errorCookie;
418cfbde714SPeter Klausler   }
4195d5b9682Speter klausler }
4205d5b9682Speter klausler 
421f3c31d70SSlava Zakharin Cookie IODEF(BeginRewind)(
4225d5b9682Speter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
4235d5b9682Speter klausler   Terminator terminator{sourceFile, sourceLine};
424cfbde714SPeter Klausler   Cookie errorCookie{nullptr};
425cfbde714SPeter Klausler   if (ExternalFileUnit *
42671e0261fSSlava Zakharin       unit{GetOrCreateUnit(unitNumber, Direction::Input,
42771e0261fSSlava Zakharin           Fortran::common::nullopt, terminator, errorCookie)}) {
42879f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
42979f6b812SPeter Klausler       return &child->BeginIoStatement<ErroneousIoStatementState>(
43079f6b812SPeter Klausler           IostatBadOpOnChildUnit, nullptr /* no unit */, sourceFile,
43179f6b812SPeter Klausler           sourceLine);
43279f6b812SPeter Klausler     } else {
433921316afSPeter Klausler       return &unit->BeginIoStatement<ExternalMiscIoStatementState>(terminator,
434cfbde714SPeter Klausler           *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
43579f6b812SPeter Klausler     }
436cfbde714SPeter Klausler   } else {
437cfbde714SPeter Klausler     return errorCookie;
438cfbde714SPeter Klausler   }
4395d5b9682Speter klausler }
4405d5b9682Speter klausler 
441f3c31d70SSlava Zakharin Cookie IODEF(BeginInquireUnit)(
442675ad1bcSpeter klausler     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
443921316afSPeter Klausler   Terminator terminator{sourceFile, sourceLine};
444675ad1bcSpeter klausler   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
445b1856009SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
446b1856009SPeter Klausler       return &child->BeginIoStatement<InquireUnitState>(
447b1856009SPeter Klausler           *unit, sourceFile, sourceLine);
448b1856009SPeter Klausler     } else {
449675ad1bcSpeter klausler       return &unit->BeginIoStatement<InquireUnitState>(
450921316afSPeter Klausler           terminator, *unit, sourceFile, sourceLine);
451b1856009SPeter Klausler     }
452675ad1bcSpeter klausler   } else {
453675ad1bcSpeter klausler     // INQUIRE(UNIT=unrecognized unit)
454921316afSPeter Klausler     return &New<InquireNoUnitState>{terminator}(
455921316afSPeter Klausler         sourceFile, sourceLine, unitNumber)
456675ad1bcSpeter klausler                 .release()
457675ad1bcSpeter klausler                 ->ioStatementState();
458675ad1bcSpeter klausler   }
459675ad1bcSpeter klausler }
460675ad1bcSpeter klausler 
461f3c31d70SSlava Zakharin Cookie IODEF(BeginInquireFile)(const char *path, std::size_t pathLength,
462675ad1bcSpeter klausler     const char *sourceFile, int sourceLine) {
463921316afSPeter Klausler   Terminator terminator{sourceFile, sourceLine};
464921316afSPeter Klausler   auto trimmed{SaveDefaultCharacter(
465921316afSPeter Klausler       path, TrimTrailingSpaces(path, pathLength), terminator)};
46603c066abSPeter Klausler   if (ExternalFileUnit *
46703c066abSPeter Klausler       unit{ExternalFileUnit::LookUp(
468f3c31d70SSlava Zakharin           trimmed.get(), Fortran::runtime::strlen(trimmed.get()))}) {
469675ad1bcSpeter klausler     // INQUIRE(FILE=) to a connected unit
47079f6b812SPeter Klausler     if (ChildIo * child{unit->GetChildIo()}) {
47179f6b812SPeter Klausler       return &child->BeginIoStatement<InquireUnitState>(
47279f6b812SPeter Klausler           *unit, sourceFile, sourceLine);
47379f6b812SPeter Klausler     } else {
474675ad1bcSpeter klausler       return &unit->BeginIoStatement<InquireUnitState>(
475921316afSPeter Klausler           terminator, *unit, sourceFile, sourceLine);
47679f6b812SPeter Klausler     }
477675ad1bcSpeter klausler   } else {
478921316afSPeter Klausler     return &New<InquireUnconnectedFileState>{terminator}(
479675ad1bcSpeter klausler         std::move(trimmed), sourceFile, sourceLine)
480675ad1bcSpeter klausler                 .release()
481675ad1bcSpeter klausler                 ->ioStatementState();
482675ad1bcSpeter klausler   }
483675ad1bcSpeter klausler }
484675ad1bcSpeter klausler 
485f3c31d70SSlava Zakharin Cookie IODEF(BeginInquireIoLength)(const char *sourceFile, int sourceLine) {
486675ad1bcSpeter klausler   Terminator oom{sourceFile, sourceLine};
487675ad1bcSpeter klausler   return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine)
488675ad1bcSpeter klausler               .release()
489675ad1bcSpeter klausler               ->ioStatementState();
490675ad1bcSpeter klausler }
491675ad1bcSpeter klausler 
49295696d56Speter klausler // Control list items
49395696d56Speter klausler 
494f3c31d70SSlava Zakharin void IODEF(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr,
4953b635714Speter klausler     bool hasEnd, bool hasEor, bool hasIoMsg) {
49695696d56Speter klausler   IoErrorHandler &handler{cookie->GetIoErrorHandler()};
49795696d56Speter klausler   if (hasIoStat) {
49895696d56Speter klausler     handler.HasIoStat();
49995696d56Speter klausler   }
50095696d56Speter klausler   if (hasErr) {
50195696d56Speter klausler     handler.HasErrLabel();
50295696d56Speter klausler   }
50395696d56Speter klausler   if (hasEnd) {
50495696d56Speter klausler     handler.HasEndLabel();
50595696d56Speter klausler   }
50695696d56Speter klausler   if (hasEor) {
50795696d56Speter klausler     handler.HasEorLabel();
50895696d56Speter klausler   }
5093b635714Speter klausler   if (hasIoMsg) {
5103b635714Speter klausler     handler.HasIoMsg();
5113b635714Speter klausler   }
51295696d56Speter klausler }
51395696d56Speter klausler 
514f3c31d70SSlava Zakharin static RT_API_ATTRS bool YesOrNo(const char *keyword, std::size_t length,
515f3c31d70SSlava Zakharin     const char *what, IoErrorHandler &handler) {
51695696d56Speter klausler   static const char *keywords[]{"YES", "NO", nullptr};
51795696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
5181f879005STim Keith   case 0:
5191f879005STim Keith     return true;
5201f879005STim Keith   case 1:
5211f879005STim Keith     return false;
52295696d56Speter klausler   default:
5233b635714Speter klausler     handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what,
5243b635714Speter klausler         static_cast<int>(length), keyword);
52595696d56Speter klausler     return false;
52695696d56Speter klausler   }
52795696d56Speter klausler }
52895696d56Speter klausler 
529f3c31d70SSlava Zakharin bool IODEF(SetAdvance)(Cookie cookie, const char *keyword, std::size_t length) {
53095696d56Speter klausler   IoStatementState &io{*cookie};
531deb62f5aSPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
532deb62f5aSPeter Klausler   bool nonAdvancing{!YesOrNo(keyword, length, "ADVANCE", handler)};
533cd0a1226Speter klausler   if (nonAdvancing && io.GetConnectionState().access == Access::Direct) {
534deb62f5aSPeter Klausler     handler.SignalError("Non-advancing I/O attempted on direct access file");
535cd0a1226Speter klausler   } else {
53679f6b812SPeter Klausler     auto *unit{io.GetExternalFileUnit()};
53779f6b812SPeter Klausler     if (unit && unit->GetChildIo()) {
53879f6b812SPeter Klausler       // ADVANCE= is ignored for child I/O (12.6.4.8.3 p3)
53979f6b812SPeter Klausler     } else {
540cd0a1226Speter klausler       io.mutableModes().nonAdvancing = nonAdvancing;
5413b635714Speter klausler     }
54279f6b812SPeter Klausler   }
543deb62f5aSPeter Klausler   return !handler.InError();
54495696d56Speter klausler }
54595696d56Speter klausler 
546f3c31d70SSlava Zakharin bool IODEF(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) {
54795696d56Speter klausler   IoStatementState &io{*cookie};
54895696d56Speter klausler   static const char *keywords[]{"NULL", "ZERO", nullptr};
54995696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
5501f879005STim Keith   case 0:
5512b0b9b2eSPeter Klausler     io.mutableModes().editingFlags &= ~blankZero;
5521f879005STim Keith     return true;
5531f879005STim Keith   case 1:
5542b0b9b2eSPeter Klausler     io.mutableModes().editingFlags |= blankZero;
5551f879005STim Keith     return true;
55695696d56Speter klausler   default:
5573b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
55895696d56Speter klausler         "Invalid BLANK='%.*s'", static_cast<int>(length), keyword);
55995696d56Speter klausler     return false;
56095696d56Speter klausler   }
56195696d56Speter klausler }
56295696d56Speter klausler 
563f3c31d70SSlava Zakharin bool IODEF(SetDecimal)(Cookie cookie, const char *keyword, std::size_t length) {
56495696d56Speter klausler   IoStatementState &io{*cookie};
56595696d56Speter klausler   static const char *keywords[]{"COMMA", "POINT", nullptr};
56695696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
5671f879005STim Keith   case 0:
5682b0b9b2eSPeter Klausler     io.mutableModes().editingFlags |= decimalComma;
5691f879005STim Keith     return true;
5701f879005STim Keith   case 1:
5712b0b9b2eSPeter Klausler     io.mutableModes().editingFlags &= ~decimalComma;
5721f879005STim Keith     return true;
57395696d56Speter klausler   default:
5743b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
57595696d56Speter klausler         "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword);
57695696d56Speter klausler     return false;
57795696d56Speter klausler   }
57895696d56Speter klausler }
57995696d56Speter klausler 
580f3c31d70SSlava Zakharin bool IODEF(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) {
58195696d56Speter klausler   IoStatementState &io{*cookie};
58295696d56Speter klausler   static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr};
58395696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
5841f879005STim Keith   case 0:
5852b0b9b2eSPeter Klausler     io.mutableModes().delim = '\'';
5861f879005STim Keith     return true;
5871f879005STim Keith   case 1:
5882b0b9b2eSPeter Klausler     io.mutableModes().delim = '"';
5891f879005STim Keith     return true;
5901f879005STim Keith   case 2:
5912b0b9b2eSPeter Klausler     io.mutableModes().delim = '\0';
5921f879005STim Keith     return true;
59395696d56Speter klausler   default:
5943b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
59595696d56Speter klausler         "Invalid DELIM='%.*s'", static_cast<int>(length), keyword);
59695696d56Speter klausler     return false;
59795696d56Speter klausler   }
59895696d56Speter klausler }
59995696d56Speter klausler 
600f3c31d70SSlava Zakharin bool IODEF(SetPad)(Cookie cookie, const char *keyword, std::size_t length) {
60195696d56Speter klausler   IoStatementState &io{*cookie};
602deb62f5aSPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
603deb62f5aSPeter Klausler   io.mutableModes().pad = YesOrNo(keyword, length, "PAD", handler);
604deb62f5aSPeter Klausler   return !handler.InError();
60595696d56Speter klausler }
60695696d56Speter klausler 
607f3c31d70SSlava Zakharin bool IODEF(SetPos)(Cookie cookie, std::int64_t pos) {
6083b635714Speter klausler   IoStatementState &io{*cookie};
609991696c2SPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
6103b635714Speter klausler   if (auto *unit{io.GetExternalFileUnit()}) {
611d771245aSPeter Klausler     return unit->SetStreamPos(pos, handler);
6122a07db4cSPeter Klausler   } else if (!io.get_if<ErroneousIoStatementState>()) {
613d771245aSPeter Klausler     handler.Crash("SetPos() called on internal unit");
6142a07db4cSPeter Klausler   }
6153b635714Speter klausler   return false;
6163b635714Speter klausler }
6173b635714Speter klausler 
618f3c31d70SSlava Zakharin bool IODEF(SetRec)(Cookie cookie, std::int64_t rec) {
6193b635714Speter klausler   IoStatementState &io{*cookie};
620991696c2SPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
6213b635714Speter klausler   if (auto *unit{io.GetExternalFileUnit()}) {
62279f6b812SPeter Klausler     if (unit->GetChildIo()) {
62379f6b812SPeter Klausler       handler.SignalError(
62479f6b812SPeter Klausler           IostatBadOpOnChildUnit, "REC= specifier on child I/O");
62579f6b812SPeter Klausler     } else {
626bf95854eSPeter Klausler       handler.HasRec();
627d771245aSPeter Klausler       unit->SetDirectRec(rec, handler);
62879f6b812SPeter Klausler     }
629d771245aSPeter Klausler   } else if (!io.get_if<ErroneousIoStatementState>()) {
630d771245aSPeter Klausler     handler.Crash("SetRec() called on internal unit");
6313b635714Speter klausler   }
6323b635714Speter klausler   return true;
6333b635714Speter klausler }
63495696d56Speter klausler 
635f3c31d70SSlava Zakharin bool IODEF(SetRound)(Cookie cookie, const char *keyword, std::size_t length) {
63695696d56Speter klausler   IoStatementState &io{*cookie};
63795696d56Speter klausler   static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE",
63895696d56Speter klausler       "PROCESSOR_DEFINED", nullptr};
63995696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
6401f879005STim Keith   case 0:
6412b0b9b2eSPeter Klausler     io.mutableModes().round = decimal::RoundUp;
6421f879005STim Keith     return true;
6431f879005STim Keith   case 1:
6442b0b9b2eSPeter Klausler     io.mutableModes().round = decimal::RoundDown;
6451f879005STim Keith     return true;
6461f879005STim Keith   case 2:
6472b0b9b2eSPeter Klausler     io.mutableModes().round = decimal::RoundToZero;
6481f879005STim Keith     return true;
6491f879005STim Keith   case 3:
6502b0b9b2eSPeter Klausler     io.mutableModes().round = decimal::RoundNearest;
6511f879005STim Keith     return true;
6521f879005STim Keith   case 4:
6532b0b9b2eSPeter Klausler     io.mutableModes().round = decimal::RoundCompatible;
6541f879005STim Keith     return true;
65595696d56Speter klausler   case 5:
6562b0b9b2eSPeter Klausler     io.mutableModes().round = executionEnvironment.defaultOutputRoundingMode;
65795696d56Speter klausler     return true;
65895696d56Speter klausler   default:
6593b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
66095696d56Speter klausler         "Invalid ROUND='%.*s'", static_cast<int>(length), keyword);
66195696d56Speter klausler     return false;
66295696d56Speter klausler   }
66395696d56Speter klausler }
66495696d56Speter klausler 
665f3c31d70SSlava Zakharin bool IODEF(SetSign)(Cookie cookie, const char *keyword, std::size_t length) {
66695696d56Speter klausler   IoStatementState &io{*cookie};
6675501c16eSPeter Klausler   static const char *keywords[]{
6685501c16eSPeter Klausler       "PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr};
66995696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
6701f879005STim Keith   case 0:
6712b0b9b2eSPeter Klausler     io.mutableModes().editingFlags |= signPlus;
6721f879005STim Keith     return true;
67395696d56Speter klausler   case 1:
67495696d56Speter klausler   case 2: // processor default is SS
6752b0b9b2eSPeter Klausler     io.mutableModes().editingFlags &= ~signPlus;
67695696d56Speter klausler     return true;
67795696d56Speter klausler   default:
6783b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
67995696d56Speter klausler         "Invalid SIGN='%.*s'", static_cast<int>(length), keyword);
68095696d56Speter klausler     return false;
68195696d56Speter klausler   }
68295696d56Speter klausler }
68395696d56Speter klausler 
684f3c31d70SSlava Zakharin bool IODEF(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) {
68595696d56Speter klausler   IoStatementState &io{*cookie};
68695696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
68795696d56Speter klausler   if (!open) {
6888f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
6898f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
69095696d56Speter klausler       io.GetIoErrorHandler().Crash(
69195696d56Speter klausler           "SetAccess() called when not in an OPEN statement");
6922a07db4cSPeter Klausler     }
6932a07db4cSPeter Klausler     return false;
6948db4dc86SPeter Klausler   } else if (open->completedOperation()) {
6958db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
6968db4dc86SPeter Klausler         "SetAccess() called after GetNewUnit() for an OPEN statement");
69795696d56Speter klausler   }
69872abc199Speter klausler   static const char *keywords[]{
69972abc199Speter klausler       "SEQUENTIAL", "DIRECT", "STREAM", "APPEND", nullptr};
70095696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
7011f879005STim Keith   case 0:
702675ad1bcSpeter klausler     open->set_access(Access::Sequential);
7031f879005STim Keith     break;
7041f879005STim Keith   case 1:
705675ad1bcSpeter klausler     open->set_access(Access::Direct);
7061f879005STim Keith     break;
7071f879005STim Keith   case 2:
708675ad1bcSpeter klausler     open->set_access(Access::Stream);
7091f879005STim Keith     break;
71072abc199Speter klausler   case 3: // Sun Fortran extension ACCESS=APPEND: treat as if POSITION=APPEND
71172abc199Speter klausler     open->set_position(Position::Append);
71272abc199Speter klausler     break;
71395696d56Speter klausler   default:
7143b635714Speter klausler     open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
7153b635714Speter klausler         static_cast<int>(length), keyword);
71695696d56Speter klausler   }
71795696d56Speter klausler   return true;
71895696d56Speter klausler }
71995696d56Speter klausler 
720f3c31d70SSlava Zakharin bool IODEF(SetAction)(Cookie cookie, const char *keyword, std::size_t length) {
72195696d56Speter klausler   IoStatementState &io{*cookie};
72295696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
72395696d56Speter klausler   if (!open) {
7248f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
7258f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
72695696d56Speter klausler       io.GetIoErrorHandler().Crash(
72795696d56Speter klausler           "SetAction() called when not in an OPEN statement");
7282a07db4cSPeter Klausler     }
7292a07db4cSPeter Klausler     return false;
7308db4dc86SPeter Klausler   } else if (open->completedOperation()) {
7318db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
7328db4dc86SPeter Klausler         "SetAction() called after GetNewUnit() for an OPEN statement");
73395696d56Speter klausler   }
73471e0261fSSlava Zakharin   Fortran::common::optional<Action> action;
73595696d56Speter klausler   static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr};
73695696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
7371f879005STim Keith   case 0:
738ea4758a1Speter klausler     action = Action::Read;
7391f879005STim Keith     break;
7401f879005STim Keith   case 1:
741ea4758a1Speter klausler     action = Action::Write;
7421f879005STim Keith     break;
7431f879005STim Keith   case 2:
744ea4758a1Speter klausler     action = Action::ReadWrite;
7451f879005STim Keith     break;
74695696d56Speter klausler   default:
7473b635714Speter klausler     open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'",
7483b635714Speter klausler         static_cast<int>(length), keyword);
74995696d56Speter klausler     return false;
75095696d56Speter klausler   }
751ea4758a1Speter klausler   RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value());
75295696d56Speter klausler   if (open->wasExtant()) {
753ea4758a1Speter klausler     if ((*action != Action::Write) != open->unit().mayRead() ||
754ea4758a1Speter klausler         (*action != Action::Read) != open->unit().mayWrite()) {
7553b635714Speter klausler       open->SignalError("ACTION= may not be changed on an open unit");
75695696d56Speter klausler     }
75795696d56Speter klausler   }
758ea4758a1Speter klausler   open->set_action(*action);
75995696d56Speter klausler   return true;
76095696d56Speter klausler }
76195696d56Speter klausler 
762f3c31d70SSlava Zakharin bool IODEF(SetAsynchronous)(
76395696d56Speter klausler     Cookie cookie, const char *keyword, std::size_t length) {
76495696d56Speter klausler   IoStatementState &io{*cookie};
765deb62f5aSPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
766deb62f5aSPeter Klausler   bool isYes{YesOrNo(keyword, length, "ASYNCHRONOUS", handler)};
767deb62f5aSPeter Klausler   if (auto *open{io.get_if<OpenStatementState>()}) {
768deb62f5aSPeter Klausler     if (open->completedOperation()) {
769deb62f5aSPeter Klausler       handler.Crash(
7708db4dc86SPeter Klausler           "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
77195696d56Speter klausler     }
772deb62f5aSPeter Klausler     open->unit().set_mayAsynchronous(isYes);
773*fee393e4SPeter Klausler   } else if (!isYes) {
774*fee393e4SPeter Klausler     // ASYNCHRONOUS='NO' is the default, so this is a no-op
775166d6ed5SPeter Klausler   } else if (auto *ext{io.get_if<ExternalIoStatementBase>()}) {
776166d6ed5SPeter Klausler     if (ext->unit().mayAsynchronous()) {
777166d6ed5SPeter Klausler       ext->SetAsynchronous();
778166d6ed5SPeter Klausler     } else {
779deb62f5aSPeter Klausler       handler.SignalError(IostatBadAsynchronous);
78095696d56Speter klausler     }
7818f3357b7SPeter Klausler   } else if (!io.get_if<NoopStatementState>() &&
7828f3357b7SPeter Klausler       !io.get_if<ErroneousIoStatementState>()) {
783*fee393e4SPeter Klausler     handler.Crash("SetAsynchronous('YES') called when not in an OPEN or "
784*fee393e4SPeter Klausler                   "external I/O statement");
785deb62f5aSPeter Klausler   }
786deb62f5aSPeter Klausler   return !handler.InError();
78795696d56Speter klausler }
78895696d56Speter klausler 
789f3c31d70SSlava Zakharin bool IODEF(SetCarriagecontrol)(
790c9637577Speter klausler     Cookie cookie, const char *keyword, std::size_t length) {
791c9637577Speter klausler   IoStatementState &io{*cookie};
792c9637577Speter klausler   auto *open{io.get_if<OpenStatementState>()};
793c9637577Speter klausler   if (!open) {
7948f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
7958f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
796c9637577Speter klausler       io.GetIoErrorHandler().Crash(
797c9637577Speter klausler           "SetCarriageControl() called when not in an OPEN statement");
7982a07db4cSPeter Klausler     }
7992a07db4cSPeter Klausler     return false;
8008db4dc86SPeter Klausler   } else if (open->completedOperation()) {
8018db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
8028db4dc86SPeter Klausler         "SetCarriageControl() called after GetNewUnit() for an OPEN statement");
803c9637577Speter klausler   }
804c9637577Speter klausler   static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr};
805c9637577Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
806c9637577Speter klausler   case 0:
807c9637577Speter klausler     return true;
808c9637577Speter klausler   case 1:
809c9637577Speter klausler   case 2:
810c9637577Speter klausler     open->SignalError(IostatErrorInKeyword,
811c9637577Speter klausler         "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length),
812c9637577Speter klausler         keyword);
813c9637577Speter klausler     return false;
814c9637577Speter klausler   default:
815c9637577Speter klausler     open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'",
816c9637577Speter klausler         static_cast<int>(length), keyword);
817c9637577Speter klausler     return false;
818c9637577Speter klausler   }
819c9637577Speter klausler }
820c9637577Speter klausler 
821f3c31d70SSlava Zakharin bool IODEF(SetConvert)(Cookie cookie, const char *keyword, std::size_t length) {
8228f2c5c43Speter klausler   IoStatementState &io{*cookie};
8238f2c5c43Speter klausler   auto *open{io.get_if<OpenStatementState>()};
8248f2c5c43Speter klausler   if (!open) {
8258f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
8268f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
8278f2c5c43Speter klausler       io.GetIoErrorHandler().Crash(
8288f2c5c43Speter klausler           "SetConvert() called when not in an OPEN statement");
8292a07db4cSPeter Klausler     }
8302a07db4cSPeter Klausler     return false;
8318db4dc86SPeter Klausler   } else if (open->completedOperation()) {
8328db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
8338db4dc86SPeter Klausler         "SetConvert() called after GetNewUnit() for an OPEN statement");
8348f2c5c43Speter klausler   }
8358f2c5c43Speter klausler   if (auto convert{GetConvertFromString(keyword, length)}) {
8368f2c5c43Speter klausler     open->set_convert(*convert);
8378f2c5c43Speter klausler     return true;
8388f2c5c43Speter klausler   } else {
8398f2c5c43Speter klausler     open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'",
8408f2c5c43Speter klausler         static_cast<int>(length), keyword);
8418f2c5c43Speter klausler     return false;
8428f2c5c43Speter klausler   }
8438f2c5c43Speter klausler }
8448f2c5c43Speter klausler 
845f3c31d70SSlava Zakharin bool IODEF(SetEncoding)(
84695696d56Speter klausler     Cookie cookie, const char *keyword, std::size_t length) {
84795696d56Speter klausler   IoStatementState &io{*cookie};
84895696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
84995696d56Speter klausler   if (!open) {
8508f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
8518f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
85295696d56Speter klausler       io.GetIoErrorHandler().Crash(
85395696d56Speter klausler           "SetEncoding() called when not in an OPEN statement");
8542a07db4cSPeter Klausler     }
8552a07db4cSPeter Klausler     return false;
8568db4dc86SPeter Klausler   } else if (open->completedOperation()) {
8578db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
8588db4dc86SPeter Klausler         "SetEncoding() called after GetNewUnit() for an OPEN statement");
85995696d56Speter klausler   }
860c9b31daeSPeter Klausler   // Allow the encoding to be changed on an open unit -- it's
861c9b31daeSPeter Klausler   // useful and safe.
86295696d56Speter klausler   static const char *keywords[]{"UTF-8", "DEFAULT", nullptr};
86395696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
8641f879005STim Keith   case 0:
865c9b31daeSPeter Klausler     open->unit().isUTF8 = true;
8661f879005STim Keith     break;
8671f879005STim Keith   case 1:
868c9b31daeSPeter Klausler     open->unit().isUTF8 = false;
8691f879005STim Keith     break;
87095696d56Speter klausler   default:
8713b635714Speter klausler     open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'",
8723b635714Speter klausler         static_cast<int>(length), keyword);
87395696d56Speter klausler   }
87495696d56Speter klausler   return true;
87595696d56Speter klausler }
87695696d56Speter klausler 
877f3c31d70SSlava Zakharin bool IODEF(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
87895696d56Speter klausler   IoStatementState &io{*cookie};
87995696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
88095696d56Speter klausler   if (!open) {
8818f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
8828f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
88395696d56Speter klausler       io.GetIoErrorHandler().Crash(
884c9637577Speter klausler           "SetForm() called when not in an OPEN statement");
8852a07db4cSPeter Klausler     }
8868db4dc86SPeter Klausler   } else if (open->completedOperation()) {
8878db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
8888db4dc86SPeter Klausler         "SetForm() called after GetNewUnit() for an OPEN statement");
88995696d56Speter klausler   }
89095696d56Speter klausler   static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr};
89195696d56Speter klausler   switch (IdentifyValue(keyword, length, keywords)) {
8921f879005STim Keith   case 0:
893675ad1bcSpeter klausler     open->set_isUnformatted(false);
8941f879005STim Keith     break;
8951f879005STim Keith   case 1:
896675ad1bcSpeter klausler     open->set_isUnformatted(true);
8971f879005STim Keith     break;
89895696d56Speter klausler   default:
8993b635714Speter klausler     open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'",
9003b635714Speter klausler         static_cast<int>(length), keyword);
90195696d56Speter klausler   }
90295696d56Speter klausler   return true;
90395696d56Speter klausler }
90495696d56Speter klausler 
905f3c31d70SSlava Zakharin bool IODEF(SetPosition)(
90695696d56Speter klausler     Cookie cookie, const char *keyword, std::size_t length) {
90795696d56Speter klausler   IoStatementState &io{*cookie};
90895696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
90995696d56Speter klausler   if (!open) {
9108f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
9118f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
91295696d56Speter klausler       io.GetIoErrorHandler().Crash(
91395696d56Speter klausler           "SetPosition() called when not in an OPEN statement");
9142a07db4cSPeter Klausler     }
9152a07db4cSPeter Klausler     return false;
9168db4dc86SPeter Klausler   } else if (open->completedOperation()) {
9178db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
9188db4dc86SPeter Klausler         "SetPosition() called after GetNewUnit() for an OPEN statement");
91995696d56Speter klausler   }
92095696d56Speter klausler   static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr};
92195696d56Speter klausler   switch (IdentifyValue(keyword, length, positions)) {
9221f879005STim Keith   case 0:
9231f879005STim Keith     open->set_position(Position::AsIs);
9241f879005STim Keith     return true;
9251f879005STim Keith   case 1:
9261f879005STim Keith     open->set_position(Position::Rewind);
9271f879005STim Keith     return true;
9281f879005STim Keith   case 2:
9291f879005STim Keith     open->set_position(Position::Append);
9301f879005STim Keith     return true;
93195696d56Speter klausler   default:
9323b635714Speter klausler     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
93395696d56Speter klausler         "Invalid POSITION='%.*s'", static_cast<int>(length), keyword);
93495696d56Speter klausler   }
93595696d56Speter klausler   return true;
93695696d56Speter klausler }
93795696d56Speter klausler 
938f3c31d70SSlava Zakharin bool IODEF(SetRecl)(Cookie cookie, std::size_t n) {
93995696d56Speter klausler   IoStatementState &io{*cookie};
94095696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
94195696d56Speter klausler   if (!open) {
9428f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
9438f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
94495696d56Speter klausler       io.GetIoErrorHandler().Crash(
94595696d56Speter klausler           "SetRecl() called when not in an OPEN statement");
9462a07db4cSPeter Klausler     }
9472a07db4cSPeter Klausler     return false;
9488db4dc86SPeter Klausler   } else if (open->completedOperation()) {
9498db4dc86SPeter Klausler     io.GetIoErrorHandler().Crash(
9508db4dc86SPeter Klausler         "SetRecl() called after GetNewUnit() for an OPEN statement");
95195696d56Speter klausler   }
9525f11d38dSPeter Klausler   if (static_cast<std::int64_t>(n) <= 0) {
9533b635714Speter klausler     io.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
954e847b303SPeter Klausler     return false;
955e847b303SPeter Klausler   } else if (open->wasExtant() &&
956e847b303SPeter Klausler       open->unit().openRecl.value_or(0) != static_cast<std::int64_t>(n)) {
9573b635714Speter klausler     open->SignalError("RECL= may not be changed for an open unit");
958e847b303SPeter Klausler     return false;
959e847b303SPeter Klausler   } else {
96006ca9f24SPeter Klausler     open->unit().openRecl = n;
96195696d56Speter klausler     return true;
96295696d56Speter klausler   }
963e847b303SPeter Klausler }
96495696d56Speter klausler 
965f3c31d70SSlava Zakharin bool IODEF(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) {
96695696d56Speter klausler   IoStatementState &io{*cookie};
96795696d56Speter klausler   if (auto *open{io.get_if<OpenStatementState>()}) {
9688db4dc86SPeter Klausler     if (open->completedOperation()) {
9698db4dc86SPeter Klausler       io.GetIoErrorHandler().Crash(
9708db4dc86SPeter Klausler           "SetStatus() called after GetNewUnit() for an OPEN statement");
9718db4dc86SPeter Klausler     }
97295696d56Speter klausler     static const char *statuses[]{
97395696d56Speter klausler         "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr};
97495696d56Speter klausler     switch (IdentifyValue(keyword, length, statuses)) {
9751f879005STim Keith     case 0:
9761f879005STim Keith       open->set_status(OpenStatus::Old);
9771f879005STim Keith       return true;
9781f879005STim Keith     case 1:
9791f879005STim Keith       open->set_status(OpenStatus::New);
9801f879005STim Keith       return true;
9811f879005STim Keith     case 2:
9821f879005STim Keith       open->set_status(OpenStatus::Scratch);
9831f879005STim Keith       return true;
9841f879005STim Keith     case 3:
9851f879005STim Keith       open->set_status(OpenStatus::Replace);
9861f879005STim Keith       return true;
9871f879005STim Keith     case 4:
9881f879005STim Keith       open->set_status(OpenStatus::Unknown);
9891f879005STim Keith       return true;
99095696d56Speter klausler     default:
9913b635714Speter klausler       io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
99295696d56Speter klausler           "Invalid STATUS='%.*s'", static_cast<int>(length), keyword);
99395696d56Speter klausler     }
99495696d56Speter klausler     return false;
99595696d56Speter klausler   }
99695696d56Speter klausler   if (auto *close{io.get_if<CloseStatementState>()}) {
99795696d56Speter klausler     static const char *statuses[]{"KEEP", "DELETE", nullptr};
99895696d56Speter klausler     switch (IdentifyValue(keyword, length, statuses)) {
9991f879005STim Keith     case 0:
10001f879005STim Keith       close->set_status(CloseStatus::Keep);
10011f879005STim Keith       return true;
10021f879005STim Keith     case 1:
10031f879005STim Keith       close->set_status(CloseStatus::Delete);
10041f879005STim Keith       return true;
100595696d56Speter klausler     default:
10063b635714Speter klausler       io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
100795696d56Speter klausler           "Invalid STATUS='%.*s'", static_cast<int>(length), keyword);
100895696d56Speter klausler     }
100995696d56Speter klausler     return false;
101095696d56Speter klausler   }
10112a07db4cSPeter Klausler   if (io.get_if<NoopStatementState>() ||
10122a07db4cSPeter Klausler       io.get_if<ErroneousIoStatementState>()) {
101395696d56Speter klausler     return true; // don't bother validating STATUS= in a no-op CLOSE
101495696d56Speter klausler   }
101595696d56Speter klausler   io.GetIoErrorHandler().Crash(
101695696d56Speter klausler       "SetStatus() called when not in an OPEN or CLOSE statement");
101795696d56Speter klausler }
101895696d56Speter klausler 
1019f3c31d70SSlava Zakharin bool IODEF(SetFile)(Cookie cookie, const char *path, std::size_t chars) {
102095696d56Speter klausler   IoStatementState &io{*cookie};
102195696d56Speter klausler   if (auto *open{io.get_if<OpenStatementState>()}) {
10228db4dc86SPeter Klausler     if (open->completedOperation()) {
10238db4dc86SPeter Klausler       io.GetIoErrorHandler().Crash(
10248db4dc86SPeter Klausler           "SetFile() called after GetNewUnit() for an OPEN statement");
10258db4dc86SPeter Klausler     }
1026675ad1bcSpeter klausler     open->set_path(path, chars);
102795696d56Speter klausler     return true;
10288f3357b7SPeter Klausler   } else if (!io.get_if<NoopStatementState>() &&
10298f3357b7SPeter Klausler       !io.get_if<ErroneousIoStatementState>()) {
103095696d56Speter klausler     io.GetIoErrorHandler().Crash(
103195696d56Speter klausler         "SetFile() called when not in an OPEN statement");
10322a07db4cSPeter Klausler   }
103395696d56Speter klausler   return false;
103495696d56Speter klausler }
103595696d56Speter klausler 
1036f3c31d70SSlava Zakharin bool IODEF(GetNewUnit)(Cookie cookie, int &unit, int kind) {
103795696d56Speter klausler   IoStatementState &io{*cookie};
103895696d56Speter klausler   auto *open{io.get_if<OpenStatementState>()};
103995696d56Speter klausler   if (!open) {
10408f3357b7SPeter Klausler     if (!io.get_if<NoopStatementState>() &&
10418f3357b7SPeter Klausler         !io.get_if<ErroneousIoStatementState>()) {
104295696d56Speter klausler       io.GetIoErrorHandler().Crash(
104395696d56Speter klausler           "GetNewUnit() called when not in an OPEN statement");
10442a07db4cSPeter Klausler     }
10452a07db4cSPeter Klausler     return false;
10468db4dc86SPeter Klausler   } else if (!open->InError()) {
10478db4dc86SPeter Klausler     open->CompleteOperation();
10488db4dc86SPeter Klausler   }
10498db4dc86SPeter Klausler   if (open->InError()) {
10508db4dc86SPeter Klausler     // A failed OPEN(NEWUNIT=n) does not modify 'n'
10518db4dc86SPeter Klausler     return false;
105295696d56Speter klausler   }
105373b193aeSPeter Klausler   std::int64_t result{open->unit().unitNumber()};
105473b193aeSPeter Klausler   if (!SetInteger(unit, kind, result)) {
1055e3550f19SPeter Steinfeld     open->SignalError("GetNewUnit(): bad INTEGER kind(%d) or out-of-range "
105673b193aeSPeter Klausler                       "value(%jd) for result",
105773b193aeSPeter Klausler         kind, static_cast<std::intmax_t>(result));
105895696d56Speter klausler   }
105995696d56Speter klausler   return true;
106095696d56Speter klausler }
106195696d56Speter klausler 
106295696d56Speter klausler // Data transfers
106395696d56Speter klausler 
1064f3c31d70SSlava Zakharin bool IODEF(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
1065cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
106695696d56Speter klausler }
106795696d56Speter klausler 
1068f3c31d70SSlava Zakharin bool IODEF(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
1069cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
10708f2c5c43Speter klausler }
10718f2c5c43Speter klausler 
1072f3c31d70SSlava Zakharin bool IODEF(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
10732a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputInteger")) {
10742a07db4cSPeter Klausler     return false;
10752a07db4cSPeter Klausler   }
10763ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1077cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1078cc01194cSpeter klausler   descriptor.Establish(
1079cc01194cSpeter klausler       TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0);
1080cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
10813bc2ae95Speter klausler }
10823bc2ae95Speter klausler 
1083f3c31d70SSlava Zakharin bool IODEF(InputReal32)(Cookie cookie, float &x) {
10842a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputReal32")) {
10852a07db4cSPeter Klausler     return false;
10862a07db4cSPeter Klausler   }
10873ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1088cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1089cc01194cSpeter klausler   descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
1090cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
10913b635714Speter klausler }
10923b635714Speter klausler 
1093f3c31d70SSlava Zakharin bool IODEF(InputReal64)(Cookie cookie, double &x) {
10942a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputReal64")) {
10952a07db4cSPeter Klausler     return false;
10962a07db4cSPeter Klausler   }
10973ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1098cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1099cc01194cSpeter klausler   descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
1100cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
11013bc2ae95Speter klausler }
11023bc2ae95Speter klausler 
1103f3c31d70SSlava Zakharin bool IODEF(InputComplex32)(Cookie cookie, float z[2]) {
11042a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32")) {
11052a07db4cSPeter Klausler     return false;
11062a07db4cSPeter Klausler   }
11073ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1108cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1109cc01194cSpeter klausler   descriptor.Establish(
1110cc01194cSpeter klausler       TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0);
1111cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
11123bc2ae95Speter klausler }
11133bc2ae95Speter klausler 
1114f3c31d70SSlava Zakharin bool IODEF(InputComplex64)(Cookie cookie, double z[2]) {
11152a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64")) {
11162a07db4cSPeter Klausler     return false;
11172a07db4cSPeter Klausler   }
11183ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1119cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1120cc01194cSpeter klausler   descriptor.Establish(
1121cc01194cSpeter klausler       TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0);
1122cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1123f7be2518Speter klausler }
1124f7be2518Speter klausler 
1125f3c31d70SSlava Zakharin bool IODEF(OutputCharacter)(
1126cdfb95adSpeter klausler     Cookie cookie, const char *x, std::size_t length, int kind) {
11272a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter")) {
11282a07db4cSPeter Klausler     return false;
11292a07db4cSPeter Klausler   }
11303ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1131cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1132cc01194cSpeter klausler   descriptor.Establish(
1133cdfb95adSpeter klausler       kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
1134cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1135f7be2518Speter klausler }
11363b635714Speter klausler 
1137f3c31d70SSlava Zakharin bool IODEF(InputCharacter)(
1138cdfb95adSpeter klausler     Cookie cookie, char *x, std::size_t length, int kind) {
11392a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter")) {
11402a07db4cSPeter Klausler     return false;
11412a07db4cSPeter Klausler   }
11423ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1143cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1144cdfb95adSpeter klausler   descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0);
1145cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
114695696d56Speter klausler }
1147f7be2518Speter klausler 
1148f3c31d70SSlava Zakharin bool IODEF(InputAscii)(Cookie cookie, char *x, std::size_t length) {
1149cea8b8a7SPeter Klausler   return IONAME(InputCharacter)(cookie, x, length, 1);
1150cdfb95adSpeter klausler }
1151cdfb95adSpeter klausler 
1152f3c31d70SSlava Zakharin bool IODEF(InputLogical)(Cookie cookie, bool &truth) {
11532a07db4cSPeter Klausler   if (!cookie->CheckFormattedStmtType<Direction::Input>("InputLogical")) {
11542a07db4cSPeter Klausler     return false;
11552a07db4cSPeter Klausler   }
11563ada883fSPeter Klausler   StaticDescriptor<0> staticDescriptor;
1157cc01194cSpeter klausler   Descriptor &descriptor{staticDescriptor.descriptor()};
1158cc01194cSpeter klausler   descriptor.Establish(
1159cdfb95adSpeter klausler       TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
1160cc01194cSpeter klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
11613b635714Speter klausler }
11623b635714Speter klausler 
1163f3c31d70SSlava Zakharin bool IODEF(OutputDerivedType)(Cookie cookie, const Descriptor &descriptor,
11647cf1608bSPeter Klausler     const NonTbpDefinedIoTable *table) {
11657cf1608bSPeter Klausler   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor, table);
116609b00ab4SPeter Klausler }
116709b00ab4SPeter Klausler 
1168f3c31d70SSlava Zakharin bool IODEF(InputDerivedType)(Cookie cookie, const Descriptor &descriptor,
11697cf1608bSPeter Klausler     const NonTbpDefinedIoTable *table) {
11707cf1608bSPeter Klausler   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor, table);
117109b00ab4SPeter Klausler }
117209b00ab4SPeter Klausler 
1173f3c31d70SSlava Zakharin std::size_t IODEF(GetSize)(Cookie cookie) {
11744393e377Speter klausler   IoStatementState &io{*cookie};
11758db4dc86SPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
11768db4dc86SPeter Klausler   if (!handler.InError()) {
11778db4dc86SPeter Klausler     io.CompleteOperation();
11788db4dc86SPeter Klausler   }
11794393e377Speter klausler   if (const auto *formatted{
11804393e377Speter klausler           io.get_if<FormattedIoStatementState<Direction::Input>>()}) {
11814393e377Speter klausler     return formatted->GetEditDescriptorChars();
11828f3357b7SPeter Klausler   } else if (!io.get_if<NoopStatementState>() &&
11838f3357b7SPeter Klausler       !io.get_if<ErroneousIoStatementState>()) {
11842a07db4cSPeter Klausler     handler.Crash("GetIoSize() called for an I/O statement that is not a "
11852a07db4cSPeter Klausler                   "formatted READ()");
11864393e377Speter klausler   }
11874393e377Speter klausler   return 0;
11884393e377Speter klausler }
11894393e377Speter klausler 
1190f3c31d70SSlava Zakharin std::size_t IODEF(GetIoLength)(Cookie cookie) {
11914393e377Speter klausler   IoStatementState &io{*cookie};
11928db4dc86SPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
11938db4dc86SPeter Klausler   if (!handler.InError()) {
11948db4dc86SPeter Klausler     io.CompleteOperation();
11958db4dc86SPeter Klausler   }
11964393e377Speter klausler   if (const auto *inq{io.get_if<InquireIOLengthState>()}) {
11974393e377Speter klausler     return inq->bytes();
11988f3357b7SPeter Klausler   } else if (!io.get_if<NoopStatementState>() &&
11998f3357b7SPeter Klausler       !io.get_if<ErroneousIoStatementState>()) {
12008db4dc86SPeter Klausler     handler.Crash("GetIoLength() called for an I/O statement that is not "
12018db4dc86SPeter Klausler                   "INQUIRE(IOLENGTH=)");
12022a07db4cSPeter Klausler   }
12034393e377Speter klausler   return 0;
12044393e377Speter klausler }
12054393e377Speter klausler 
1206f3c31d70SSlava Zakharin void IODEF(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
12078db4dc86SPeter Klausler   IoStatementState &io{*cookie};
12088db4dc86SPeter Klausler   IoErrorHandler &handler{io.GetIoErrorHandler()};
12098db4dc86SPeter Klausler   if (!handler.InError()) {
12108db4dc86SPeter Klausler     io.CompleteOperation();
12118db4dc86SPeter Klausler   }
1212e81c96d6Speter klausler   if (handler.InError()) { // leave "msg" alone when no error
12133b635714Speter klausler     handler.GetIoMsg(msg, length);
12143b635714Speter klausler   }
1215f7be2518Speter klausler }
1216f7be2518Speter klausler 
1217f3c31d70SSlava Zakharin AsynchronousId IODEF(GetAsynchronousId)(Cookie cookie) {
12184679132aSjeanPerier   IoStatementState &io{*cookie};
12194679132aSjeanPerier   IoErrorHandler &handler{io.GetIoErrorHandler()};
12204679132aSjeanPerier   if (auto *ext{io.get_if<ExternalIoStatementBase>()}) {
12214679132aSjeanPerier     return ext->asynchronousID();
12224679132aSjeanPerier   } else if (!io.get_if<NoopStatementState>() &&
12234679132aSjeanPerier       !io.get_if<ErroneousIoStatementState>()) {
12244679132aSjeanPerier     handler.Crash(
12254679132aSjeanPerier         "GetAsynchronousId() called when not in an external I/O statement");
12264679132aSjeanPerier   }
12274679132aSjeanPerier   return 0;
12284679132aSjeanPerier }
12294679132aSjeanPerier 
1230f3c31d70SSlava Zakharin bool IODEF(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry,
1231675ad1bcSpeter klausler     char *result, std::size_t length) {
1232675ad1bcSpeter klausler   IoStatementState &io{*cookie};
1233675ad1bcSpeter klausler   return io.Inquire(inquiry, result, length);
1234675ad1bcSpeter klausler }
1235675ad1bcSpeter klausler 
1236f3c31d70SSlava Zakharin bool IODEF(InquireLogical)(
1237675ad1bcSpeter klausler     Cookie cookie, InquiryKeywordHash inquiry, bool &result) {
1238675ad1bcSpeter klausler   IoStatementState &io{*cookie};
1239675ad1bcSpeter klausler   return io.Inquire(inquiry, result);
1240675ad1bcSpeter klausler }
1241675ad1bcSpeter klausler 
1242f3c31d70SSlava Zakharin bool IODEF(InquirePendingId)(Cookie cookie, AsynchronousId id, bool &result) {
1243675ad1bcSpeter klausler   IoStatementState &io{*cookie};
1244675ad1bcSpeter klausler   return io.Inquire(HashInquiryKeyword("PENDING"), id, result);
1245675ad1bcSpeter klausler }
1246675ad1bcSpeter klausler 
1247f3c31d70SSlava Zakharin bool IODEF(InquireInteger64)(
1248675ad1bcSpeter klausler     Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) {
1249675ad1bcSpeter klausler   IoStatementState &io{*cookie};
1250e468f075SPeter Klausler   std::int64_t n{0}; // safe "undefined" value
1251675ad1bcSpeter klausler   if (io.Inquire(inquiry, n)) {
125273b193aeSPeter Klausler     if (SetInteger(result, kind, n)) {
1253675ad1bcSpeter klausler       return true;
1254675ad1bcSpeter klausler     }
125573b193aeSPeter Klausler     io.GetIoErrorHandler().SignalError(
1256e3550f19SPeter Steinfeld         "InquireInteger64(): bad INTEGER kind(%d) or out-of-range "
1257e468f075SPeter Klausler         "value(%jd) for result",
125873b193aeSPeter Klausler         kind, static_cast<std::intmax_t>(n));
125973b193aeSPeter Klausler   }
1260675ad1bcSpeter klausler   return false;
1261675ad1bcSpeter klausler }
1262675ad1bcSpeter klausler 
1263c58c64d0SJean Perier template <typename INT>
1264f3c31d70SSlava Zakharin static RT_API_ATTRS enum Iostat CheckUnitNumberInRangeImpl(INT unit,
1265f3c31d70SSlava Zakharin     bool handleError, char *ioMsg, std::size_t ioMsgLength,
1266f3c31d70SSlava Zakharin     const char *sourceFile, int sourceLine) {
12679cfa899bSJean Perier   static_assert(sizeof(INT) >= sizeof(ExternalUnit),
12689cfa899bSJean Perier       "only intended to be used when the INT to ExternalUnit conversion is "
12699cfa899bSJean Perier       "narrowing");
1270c58c64d0SJean Perier   if (unit != static_cast<ExternalUnit>(unit)) {
1271c58c64d0SJean Perier     Terminator oom{sourceFile, sourceLine};
1272c58c64d0SJean Perier     IoErrorHandler errorHandler{oom};
1273c58c64d0SJean Perier     if (handleError) {
1274c58c64d0SJean Perier       errorHandler.HasIoStat();
1275c58c64d0SJean Perier       if (ioMsg) {
1276c58c64d0SJean Perier         errorHandler.HasIoMsg();
1277c58c64d0SJean Perier       }
1278c58c64d0SJean Perier     }
1279c58c64d0SJean Perier     // Only provide the bad unit number in the message if SignalError can print
1280c58c64d0SJean Perier     // it accurately. Otherwise, the generic IostatUnitOverflow message will be
1281c58c64d0SJean Perier     // used.
12821c35c1a7SPeter Klausler     if constexpr (sizeof(INT) > sizeof(std::intmax_t)) {
12831c35c1a7SPeter Klausler       errorHandler.SignalError(IostatUnitOverflow);
12841c35c1a7SPeter Klausler     } else if (static_cast<std::intmax_t>(unit) == unit) {
1285c58c64d0SJean Perier       errorHandler.SignalError(IostatUnitOverflow,
1286c58c64d0SJean Perier           "UNIT number %jd is out of range", static_cast<std::intmax_t>(unit));
1287c58c64d0SJean Perier     } else {
1288c58c64d0SJean Perier       errorHandler.SignalError(IostatUnitOverflow);
1289c58c64d0SJean Perier     }
1290c58c64d0SJean Perier     if (ioMsg) {
1291c58c64d0SJean Perier       errorHandler.GetIoMsg(ioMsg, ioMsgLength);
1292c58c64d0SJean Perier     }
1293c58c64d0SJean Perier     return static_cast<enum Iostat>(errorHandler.GetIoStat());
1294c58c64d0SJean Perier   }
1295c58c64d0SJean Perier   return IostatOk;
1296c58c64d0SJean Perier }
1297c58c64d0SJean Perier 
1298f3c31d70SSlava Zakharin enum Iostat IODEF(CheckUnitNumberInRange64)(std::int64_t unit, bool handleError,
1299f3c31d70SSlava Zakharin     char *ioMsg, std::size_t ioMsgLength, const char *sourceFile,
1300f3c31d70SSlava Zakharin     int sourceLine) {
1301c58c64d0SJean Perier   return CheckUnitNumberInRangeImpl(
1302c58c64d0SJean Perier       unit, handleError, ioMsg, ioMsgLength, sourceFile, sourceLine);
1303c58c64d0SJean Perier }
1304c58c64d0SJean Perier 
1305c58c64d0SJean Perier #ifdef __SIZEOF_INT128__
1306f3c31d70SSlava Zakharin enum Iostat IODEF(CheckUnitNumberInRange128)(common::int128_t unit,
1307c58c64d0SJean Perier     bool handleError, char *ioMsg, std::size_t ioMsgLength,
1308c58c64d0SJean Perier     const char *sourceFile, int sourceLine) {
1309c58c64d0SJean Perier   return CheckUnitNumberInRangeImpl(
1310c58c64d0SJean Perier       unit, handleError, ioMsg, ioMsgLength, sourceFile, sourceLine);
1311c58c64d0SJean Perier }
1312c58c64d0SJean Perier #endif
1313c58c64d0SJean Perier 
131486e511bcSSlava Zakharin RT_EXT_API_GROUP_END
13151f879005STim Keith } // namespace Fortran::runtime::io
1316