• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- runtime/io-api.cpp --------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Implements the I/O statement API
10 
11 #include "io-api.h"
12 #include "descriptor-io.h"
13 #include "descriptor.h"
14 #include "edit-input.h"
15 #include "edit-output.h"
16 #include "environment.h"
17 #include "format.h"
18 #include "io-stmt.h"
19 #include "memory.h"
20 #include "terminator.h"
21 #include "tools.h"
22 #include "unit.h"
23 #include <cstdlib>
24 #include <memory>
25 
26 namespace Fortran::runtime::io {
27 
InquiryKeywordHashDecode(char * buffer,std::size_t n,InquiryKeywordHash hash)28 const char *InquiryKeywordHashDecode(
29     char *buffer, std::size_t n, InquiryKeywordHash hash) {
30   if (n < 1) {
31     return nullptr;
32   }
33   char *p{buffer + n};
34   *--p = '\0';
35   while (hash > 1) {
36     if (p < buffer) {
37       return nullptr;
38     }
39     *--p = 'A' + (hash % 26);
40     hash /= 26;
41   }
42   return hash == 1 ? p : nullptr;
43 }
44 
45 template <Direction DIR>
BeginInternalArrayListIO(const Descriptor & descriptor,void **,std::size_t,const char * sourceFile,int sourceLine)46 Cookie BeginInternalArrayListIO(const Descriptor &descriptor,
47     void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
48     const char *sourceFile, int sourceLine) {
49   Terminator oom{sourceFile, sourceLine};
50   return &New<InternalListIoStatementState<DIR>>{oom}(
51       descriptor, sourceFile, sourceLine)
52               .release()
53               ->ioStatementState();
54 }
55 
IONAME(BeginInternalArrayListOutput)56 Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &descriptor,
57     void **scratchArea, std::size_t scratchBytes, const char *sourceFile,
58     int sourceLine) {
59   return BeginInternalArrayListIO<Direction::Output>(
60       descriptor, scratchArea, scratchBytes, sourceFile, sourceLine);
61 }
62 
IONAME(BeginInternalArrayListInput)63 Cookie IONAME(BeginInternalArrayListInput)(const Descriptor &descriptor,
64     void **scratchArea, std::size_t scratchBytes, const char *sourceFile,
65     int sourceLine) {
66   return BeginInternalArrayListIO<Direction::Input>(
67       descriptor, scratchArea, scratchBytes, sourceFile, sourceLine);
68 }
69 
70 template <Direction DIR>
BeginInternalArrayFormattedIO(const Descriptor & descriptor,const char * format,std::size_t formatLength,void **,std::size_t,const char * sourceFile,int sourceLine)71 Cookie BeginInternalArrayFormattedIO(const Descriptor &descriptor,
72     const char *format, std::size_t formatLength, void ** /*scratchArea*/,
73     std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
74   Terminator oom{sourceFile, sourceLine};
75   return &New<InternalFormattedIoStatementState<DIR>>{oom}(
76       descriptor, format, formatLength, sourceFile, sourceLine)
77               .release()
78               ->ioStatementState();
79 }
80 
IONAME(BeginInternalArrayFormattedOutput)81 Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &descriptor,
82     const char *format, std::size_t formatLength, void **scratchArea,
83     std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
84   return BeginInternalArrayFormattedIO<Direction::Output>(descriptor, format,
85       formatLength, scratchArea, scratchBytes, sourceFile, sourceLine);
86 }
87 
IONAME(BeginInternalArrayFormattedInput)88 Cookie IONAME(BeginInternalArrayFormattedInput)(const Descriptor &descriptor,
89     const char *format, std::size_t formatLength, void **scratchArea,
90     std::size_t scratchBytes, const char *sourceFile, int sourceLine) {
91   return BeginInternalArrayFormattedIO<Direction::Input>(descriptor, format,
92       formatLength, scratchArea, scratchBytes, sourceFile, sourceLine);
93 }
94 
95 template <Direction DIR>
BeginInternalListIO(std::conditional_t<DIR==Direction::Input,const char,char> * internal,std::size_t internalLength,void **,std::size_t,const char * sourceFile,int sourceLine)96 Cookie BeginInternalListIO(
97     std::conditional_t<DIR == Direction::Input, const char, char> *internal,
98     std::size_t internalLength, void ** /*scratchArea*/,
99     std::size_t /*scratchBytes*/, const char *sourceFile, int sourceLine) {
100   Terminator oom{sourceFile, sourceLine};
101   return &New<InternalListIoStatementState<DIR>>{oom}(
102       internal, internalLength, sourceFile, sourceLine)
103               .release()
104               ->ioStatementState();
105 }
106 
IONAME(BeginInternalListOutput)107 Cookie IONAME(BeginInternalListOutput)(char *internal,
108     std::size_t internalLength, void **scratchArea, std::size_t scratchBytes,
109     const char *sourceFile, int sourceLine) {
110   return BeginInternalListIO<Direction::Output>(internal, internalLength,
111       scratchArea, scratchBytes, sourceFile, sourceLine);
112 }
113 
IONAME(BeginInternalListInput)114 Cookie IONAME(BeginInternalListInput)(const char *internal,
115     std::size_t internalLength, void **scratchArea, std::size_t scratchBytes,
116     const char *sourceFile, int sourceLine) {
117   return BeginInternalListIO<Direction::Input>(internal, internalLength,
118       scratchArea, scratchBytes, sourceFile, sourceLine);
119 }
120 
121 template <Direction DIR>
BeginInternalFormattedIO(std::conditional_t<DIR==Direction::Input,const char,char> * internal,std::size_t internalLength,const char * format,std::size_t formatLength,void **,std::size_t,const char * sourceFile,int sourceLine)122 Cookie BeginInternalFormattedIO(
123     std::conditional_t<DIR == Direction::Input, const char, char> *internal,
124     std::size_t internalLength, const char *format, std::size_t formatLength,
125     void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
126     const char *sourceFile, int sourceLine) {
127   Terminator oom{sourceFile, sourceLine};
128   return &New<InternalFormattedIoStatementState<DIR>>{oom}(
129       internal, internalLength, format, formatLength, sourceFile, sourceLine)
130               .release()
131               ->ioStatementState();
132 }
133 
IONAME(BeginInternalFormattedOutput)134 Cookie IONAME(BeginInternalFormattedOutput)(char *internal,
135     std::size_t internalLength, const char *format, std::size_t formatLength,
136     void **scratchArea, std::size_t scratchBytes, const char *sourceFile,
137     int sourceLine) {
138   return BeginInternalFormattedIO<Direction::Output>(internal, internalLength,
139       format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine);
140 }
141 
IONAME(BeginInternalFormattedInput)142 Cookie IONAME(BeginInternalFormattedInput)(const char *internal,
143     std::size_t internalLength, const char *format, std::size_t formatLength,
144     void **scratchArea, std::size_t scratchBytes, const char *sourceFile,
145     int sourceLine) {
146   return BeginInternalFormattedIO<Direction::Input>(internal, internalLength,
147       format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine);
148 }
149 
150 template <Direction DIR>
BeginExternalListIO(ExternalUnit unitNumber,const char * sourceFile,int sourceLine)151 Cookie BeginExternalListIO(
152     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
153   Terminator terminator{sourceFile, sourceLine};
154   if (unitNumber == DefaultUnit) {
155     unitNumber = DIR == Direction::Input ? 5 : 6;
156   }
157   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
158       unitNumber, DIR, false /*formatted*/, terminator)};
159   if (unit.access == Access::Direct) {
160     terminator.Crash("List-directed I/O attempted on direct access file");
161     return nullptr;
162   }
163   if (unit.isUnformatted) {
164     terminator.Crash("List-directed I/O attempted on unformatted file");
165     return nullptr;
166   }
167   IoErrorHandler handler{terminator};
168   unit.SetDirection(DIR, handler);
169   IoStatementState &io{unit.BeginIoStatement<ExternalListIoStatementState<DIR>>(
170       unit, sourceFile, sourceLine)};
171   return &io;
172 }
173 
IONAME(BeginExternalListOutput)174 Cookie IONAME(BeginExternalListOutput)(
175     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
176   return BeginExternalListIO<Direction::Output>(
177       unitNumber, sourceFile, sourceLine);
178 }
179 
IONAME(BeginExternalListInput)180 Cookie IONAME(BeginExternalListInput)(
181     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
182   return BeginExternalListIO<Direction::Input>(
183       unitNumber, sourceFile, sourceLine);
184 }
185 
186 template <Direction DIR>
BeginExternalFormattedIO(const char * format,std::size_t formatLength,ExternalUnit unitNumber,const char * sourceFile,int sourceLine)187 Cookie BeginExternalFormattedIO(const char *format, std::size_t formatLength,
188     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
189   Terminator terminator{sourceFile, sourceLine};
190   if (unitNumber == DefaultUnit) {
191     unitNumber = DIR == Direction::Input ? 5 : 6;
192   }
193   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
194       unitNumber, DIR, false /*formatted*/, terminator)};
195   if (unit.isUnformatted) {
196     terminator.Crash("Formatted I/O attempted on unformatted file");
197     return nullptr;
198   }
199   IoErrorHandler handler{terminator};
200   unit.SetDirection(DIR, handler);
201   IoStatementState &io{
202       unit.BeginIoStatement<ExternalFormattedIoStatementState<DIR>>(
203           unit, format, formatLength, sourceFile, sourceLine)};
204   return &io;
205 }
206 
IONAME(BeginExternalFormattedOutput)207 Cookie IONAME(BeginExternalFormattedOutput)(const char *format,
208     std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile,
209     int sourceLine) {
210   return BeginExternalFormattedIO<Direction::Output>(
211       format, formatLength, unitNumber, sourceFile, sourceLine);
212 }
213 
IONAME(BeginExternalFormattedInput)214 Cookie IONAME(BeginExternalFormattedInput)(const char *format,
215     std::size_t formatLength, ExternalUnit unitNumber, const char *sourceFile,
216     int sourceLine) {
217   return BeginExternalFormattedIO<Direction::Input>(
218       format, formatLength, unitNumber, sourceFile, sourceLine);
219 }
220 
221 template <Direction DIR>
BeginUnformattedIO(ExternalUnit unitNumber,const char * sourceFile,int sourceLine)222 Cookie BeginUnformattedIO(
223     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
224   Terminator terminator{sourceFile, sourceLine};
225   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
226       unitNumber, DIR, true /*unformatted*/, terminator)};
227   if (!unit.isUnformatted) {
228     terminator.Crash("Unformatted output attempted on formatted file");
229   }
230   IoStatementState &io{unit.BeginIoStatement<UnformattedIoStatementState<DIR>>(
231       unit, sourceFile, sourceLine)};
232   IoErrorHandler handler{terminator};
233   unit.SetDirection(DIR, handler);
234   if constexpr (DIR == Direction::Output) {
235     if (unit.access == Access::Sequential && !unit.isFixedRecordLength) {
236       // Create space for (sub)record header to be completed by
237       // UnformattedIoStatementState<Direction::Output>::EndIoStatement()
238       unit.recordLength.reset(); // in case of prior BACKSPACE
239       io.Emit("\0\0\0\0", 4); // placeholder for record length header
240     }
241   }
242   return &io;
243 }
244 
IONAME(BeginUnformattedOutput)245 Cookie IONAME(BeginUnformattedOutput)(
246     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
247   return BeginUnformattedIO<Direction::Output>(
248       unitNumber, sourceFile, sourceLine);
249 }
250 
IONAME(BeginUnformattedInput)251 Cookie IONAME(BeginUnformattedInput)(
252     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
253   return BeginUnformattedIO<Direction::Input>(
254       unitNumber, sourceFile, sourceLine);
255 }
256 
IONAME(BeginOpenUnit)257 Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=)
258     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
259   bool wasExtant{false};
260   Terminator terminator{sourceFile, sourceLine};
261   ExternalFileUnit &unit{
262       ExternalFileUnit::LookUpOrCreate(unitNumber, terminator, wasExtant)};
263   return &unit.BeginIoStatement<OpenStatementState>(
264       unit, wasExtant, sourceFile, sourceLine);
265 }
266 
IONAME(BeginOpenNewUnit)267 Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j)
268     const char *sourceFile, int sourceLine) {
269   Terminator terminator{sourceFile, sourceLine};
270   bool ignored{false};
271   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreate(
272       ExternalFileUnit::NewUnit(terminator), terminator, ignored)};
273   return &unit.BeginIoStatement<OpenStatementState>(
274       unit, false /*was an existing file*/, sourceFile, sourceLine);
275 }
276 
IONAME(BeginClose)277 Cookie IONAME(BeginClose)(
278     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
279   if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) {
280     return &unit->BeginIoStatement<CloseStatementState>(
281         *unit, sourceFile, sourceLine);
282   } else {
283     // CLOSE(UNIT=bad unit) is just a no-op
284     Terminator oom{sourceFile, sourceLine};
285     return &New<NoopCloseStatementState>{oom}(sourceFile, sourceLine)
286                 .release()
287                 ->ioStatementState();
288   }
289 }
290 
IONAME(BeginFlush)291 Cookie IONAME(BeginFlush)(
292     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
293   Terminator terminator{sourceFile, sourceLine};
294   ExternalFileUnit &unit{
295       ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
296   return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
297       unit, ExternalMiscIoStatementState::Flush, sourceFile, sourceLine);
298 }
299 
IONAME(BeginBackspace)300 Cookie IONAME(BeginBackspace)(
301     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
302   Terminator terminator{sourceFile, sourceLine};
303   ExternalFileUnit &unit{
304       ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
305   return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
306       unit, ExternalMiscIoStatementState::Backspace, sourceFile, sourceLine);
307 }
308 
IONAME(BeginEndfile)309 Cookie IONAME(BeginEndfile)(
310     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
311   Terminator terminator{sourceFile, sourceLine};
312   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
313       unitNumber, Direction::Output, true /*formatted*/, terminator)};
314   return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
315       unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
316 }
317 
IONAME(BeginRewind)318 Cookie IONAME(BeginRewind)(
319     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
320   Terminator terminator{sourceFile, sourceLine};
321   ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
322       unitNumber, Direction::Input, true /*formatted*/, terminator)};
323   return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
324       unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
325 }
326 
IONAME(BeginInquireUnit)327 Cookie IONAME(BeginInquireUnit)(
328     ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
329   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
330     return &unit->BeginIoStatement<InquireUnitState>(
331         *unit, sourceFile, sourceLine);
332   } else {
333     // INQUIRE(UNIT=unrecognized unit)
334     Terminator oom{sourceFile, sourceLine};
335     return &New<InquireNoUnitState>{oom}(sourceFile, sourceLine)
336                 .release()
337                 ->ioStatementState();
338   }
339 }
340 
IONAME(BeginInquireFile)341 Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength,
342     const char *sourceFile, int sourceLine) {
343   Terminator oom{sourceFile, sourceLine};
344   auto trimmed{
345       SaveDefaultCharacter(path, TrimTrailingSpaces(path, pathLength), oom)};
346   if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(trimmed.get())}) {
347     // INQUIRE(FILE=) to a connected unit
348     return &unit->BeginIoStatement<InquireUnitState>(
349         *unit, sourceFile, sourceLine);
350   } else {
351     return &New<InquireUnconnectedFileState>{oom}(
352         std::move(trimmed), sourceFile, sourceLine)
353                 .release()
354                 ->ioStatementState();
355   }
356 }
357 
IONAME(BeginInquireIoLength)358 Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) {
359   Terminator oom{sourceFile, sourceLine};
360   return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine)
361               .release()
362               ->ioStatementState();
363 }
364 
365 // Control list items
366 
IONAME(EnableHandlers)367 void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr,
368     bool hasEnd, bool hasEor, bool hasIoMsg) {
369   IoErrorHandler &handler{cookie->GetIoErrorHandler()};
370   if (hasIoStat) {
371     handler.HasIoStat();
372   }
373   if (hasErr) {
374     handler.HasErrLabel();
375   }
376   if (hasEnd) {
377     handler.HasEndLabel();
378   }
379   if (hasEor) {
380     handler.HasEorLabel();
381   }
382   if (hasIoMsg) {
383     handler.HasIoMsg();
384   }
385 }
386 
YesOrNo(const char * keyword,std::size_t length,const char * what,IoErrorHandler & handler)387 static bool YesOrNo(const char *keyword, std::size_t length, const char *what,
388     IoErrorHandler &handler) {
389   static const char *keywords[]{"YES", "NO", nullptr};
390   switch (IdentifyValue(keyword, length, keywords)) {
391   case 0:
392     return true;
393   case 1:
394     return false;
395   default:
396     handler.SignalError(IostatErrorInKeyword, "Invalid %s='%.*s'", what,
397         static_cast<int>(length), keyword);
398     return false;
399   }
400 }
401 
IONAME(SetAdvance)402 bool IONAME(SetAdvance)(
403     Cookie cookie, const char *keyword, std::size_t length) {
404   IoStatementState &io{*cookie};
405   ConnectionState &connection{io.GetConnectionState()};
406   connection.nonAdvancing =
407       !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler());
408   if (connection.nonAdvancing && connection.access == Access::Direct) {
409     io.GetIoErrorHandler().SignalError(
410         "Non-advancing I/O attempted on direct access file");
411   }
412   return true;
413 }
414 
IONAME(SetBlank)415 bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) {
416   IoStatementState &io{*cookie};
417   ConnectionState &connection{io.GetConnectionState()};
418   static const char *keywords[]{"NULL", "ZERO", nullptr};
419   switch (IdentifyValue(keyword, length, keywords)) {
420   case 0:
421     connection.modes.editingFlags &= ~blankZero;
422     return true;
423   case 1:
424     connection.modes.editingFlags |= blankZero;
425     return true;
426   default:
427     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
428         "Invalid BLANK='%.*s'", static_cast<int>(length), keyword);
429     return false;
430   }
431 }
432 
IONAME(SetDecimal)433 bool IONAME(SetDecimal)(
434     Cookie cookie, const char *keyword, std::size_t length) {
435   IoStatementState &io{*cookie};
436   ConnectionState &connection{io.GetConnectionState()};
437   static const char *keywords[]{"COMMA", "POINT", nullptr};
438   switch (IdentifyValue(keyword, length, keywords)) {
439   case 0:
440     connection.modes.editingFlags |= decimalComma;
441     return true;
442   case 1:
443     connection.modes.editingFlags &= ~decimalComma;
444     return true;
445   default:
446     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
447         "Invalid DECIMAL='%.*s'", static_cast<int>(length), keyword);
448     return false;
449   }
450 }
451 
IONAME(SetDelim)452 bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) {
453   IoStatementState &io{*cookie};
454   ConnectionState &connection{io.GetConnectionState()};
455   static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr};
456   switch (IdentifyValue(keyword, length, keywords)) {
457   case 0:
458     connection.modes.delim = '\'';
459     return true;
460   case 1:
461     connection.modes.delim = '"';
462     return true;
463   case 2:
464     connection.modes.delim = '\0';
465     return true;
466   default:
467     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
468         "Invalid DELIM='%.*s'", static_cast<int>(length), keyword);
469     return false;
470   }
471 }
472 
IONAME(SetPad)473 bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) {
474   IoStatementState &io{*cookie};
475   ConnectionState &connection{io.GetConnectionState()};
476   connection.modes.pad =
477       YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler());
478   return true;
479 }
480 
IONAME(SetPos)481 bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) {
482   IoStatementState &io{*cookie};
483   ConnectionState &connection{io.GetConnectionState()};
484   if (connection.access != Access::Stream) {
485     io.GetIoErrorHandler().SignalError(
486         "REC= may not appear unless ACCESS='STREAM'");
487     return false;
488   }
489   if (pos < 1) {
490     io.GetIoErrorHandler().SignalError(
491         "POS=%zd is invalid", static_cast<std::intmax_t>(pos));
492     return false;
493   }
494   if (auto *unit{io.GetExternalFileUnit()}) {
495     unit->SetPosition(pos);
496     return true;
497   }
498   io.GetIoErrorHandler().Crash("SetPos() on internal unit");
499   return false;
500 }
501 
IONAME(SetRec)502 bool IONAME(SetRec)(Cookie cookie, std::int64_t rec) {
503   IoStatementState &io{*cookie};
504   ConnectionState &connection{io.GetConnectionState()};
505   if (connection.access != Access::Direct) {
506     io.GetIoErrorHandler().SignalError(
507         "REC= may not appear unless ACCESS='DIRECT'");
508     return false;
509   }
510   if (!connection.isFixedRecordLength || !connection.recordLength) {
511     io.GetIoErrorHandler().SignalError("RECL= was not specified");
512     return false;
513   }
514   if (rec < 1) {
515     io.GetIoErrorHandler().SignalError(
516         "REC=%zd is invalid", static_cast<std::intmax_t>(rec));
517     return false;
518   }
519   connection.currentRecordNumber = rec;
520   if (auto *unit{io.GetExternalFileUnit()}) {
521     unit->SetPosition((rec - 1) * *connection.recordLength);
522   }
523   return true;
524 }
525 
IONAME(SetRound)526 bool IONAME(SetRound)(Cookie cookie, const char *keyword, std::size_t length) {
527   IoStatementState &io{*cookie};
528   ConnectionState &connection{io.GetConnectionState()};
529   static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE",
530       "PROCESSOR_DEFINED", nullptr};
531   switch (IdentifyValue(keyword, length, keywords)) {
532   case 0:
533     connection.modes.round = decimal::RoundUp;
534     return true;
535   case 1:
536     connection.modes.round = decimal::RoundDown;
537     return true;
538   case 2:
539     connection.modes.round = decimal::RoundToZero;
540     return true;
541   case 3:
542     connection.modes.round = decimal::RoundNearest;
543     return true;
544   case 4:
545     connection.modes.round = decimal::RoundCompatible;
546     return true;
547   case 5:
548     connection.modes.round = executionEnvironment.defaultOutputRoundingMode;
549     return true;
550   default:
551     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
552         "Invalid ROUND='%.*s'", static_cast<int>(length), keyword);
553     return false;
554   }
555 }
556 
IONAME(SetSign)557 bool IONAME(SetSign)(Cookie cookie, const char *keyword, std::size_t length) {
558   IoStatementState &io{*cookie};
559   ConnectionState &connection{io.GetConnectionState()};
560   static const char *keywords[]{"PLUS", "YES", "PROCESSOR_DEFINED", nullptr};
561   switch (IdentifyValue(keyword, length, keywords)) {
562   case 0:
563     connection.modes.editingFlags |= signPlus;
564     return true;
565   case 1:
566   case 2: // processor default is SS
567     connection.modes.editingFlags &= ~signPlus;
568     return true;
569   default:
570     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
571         "Invalid SIGN='%.*s'", static_cast<int>(length), keyword);
572     return false;
573   }
574 }
575 
IONAME(SetAccess)576 bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) {
577   IoStatementState &io{*cookie};
578   auto *open{io.get_if<OpenStatementState>()};
579   if (!open) {
580     io.GetIoErrorHandler().Crash(
581         "SetAccess() called when not in an OPEN statement");
582   }
583   static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr};
584   switch (IdentifyValue(keyword, length, keywords)) {
585   case 0:
586     open->set_access(Access::Sequential);
587     break;
588   case 1:
589     open->set_access(Access::Direct);
590     break;
591   case 2:
592     open->set_access(Access::Stream);
593     break;
594   default:
595     open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
596         static_cast<int>(length), keyword);
597   }
598   return true;
599 }
600 
IONAME(SetAction)601 bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) {
602   IoStatementState &io{*cookie};
603   auto *open{io.get_if<OpenStatementState>()};
604   if (!open) {
605     io.GetIoErrorHandler().Crash(
606         "SetAction() called when not in an OPEN statement");
607   }
608   std::optional<Action> action;
609   static const char *keywords[]{"READ", "WRITE", "READWRITE", nullptr};
610   switch (IdentifyValue(keyword, length, keywords)) {
611   case 0:
612     action = Action::Read;
613     break;
614   case 1:
615     action = Action::Write;
616     break;
617   case 2:
618     action = Action::ReadWrite;
619     break;
620   default:
621     open->SignalError(IostatErrorInKeyword, "Invalid ACTION='%.*s'",
622         static_cast<int>(length), keyword);
623     return false;
624   }
625   RUNTIME_CHECK(io.GetIoErrorHandler(), action.has_value());
626   if (open->wasExtant()) {
627     if ((*action != Action::Write) != open->unit().mayRead() ||
628         (*action != Action::Read) != open->unit().mayWrite()) {
629       open->SignalError("ACTION= may not be changed on an open unit");
630     }
631   }
632   open->set_action(*action);
633   return true;
634 }
635 
IONAME(SetAsynchronous)636 bool IONAME(SetAsynchronous)(
637     Cookie cookie, const char *keyword, std::size_t length) {
638   IoStatementState &io{*cookie};
639   auto *open{io.get_if<OpenStatementState>()};
640   if (!open) {
641     io.GetIoErrorHandler().Crash(
642         "SetAsynchronous() called when not in an OPEN statement");
643   }
644   static const char *keywords[]{"YES", "NO", nullptr};
645   switch (IdentifyValue(keyword, length, keywords)) {
646   case 0:
647     open->unit().set_mayAsynchronous(true);
648     return true;
649   case 1:
650     open->unit().set_mayAsynchronous(false);
651     return true;
652   default:
653     open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'",
654         static_cast<int>(length), keyword);
655     return false;
656   }
657 }
658 
IONAME(SetCarriagecontrol)659 bool IONAME(SetCarriagecontrol)(
660     Cookie cookie, const char *keyword, std::size_t length) {
661   IoStatementState &io{*cookie};
662   auto *open{io.get_if<OpenStatementState>()};
663   if (!open) {
664     io.GetIoErrorHandler().Crash(
665         "SetCarriageControl() called when not in an OPEN statement");
666   }
667   static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr};
668   switch (IdentifyValue(keyword, length, keywords)) {
669   case 0:
670     return true;
671   case 1:
672   case 2:
673     open->SignalError(IostatErrorInKeyword,
674         "Unimplemented CARRIAGECONTROL='%.*s'", static_cast<int>(length),
675         keyword);
676     return false;
677   default:
678     open->SignalError(IostatErrorInKeyword, "Invalid CARRIAGECONTROL='%.*s'",
679         static_cast<int>(length), keyword);
680     return false;
681   }
682 }
683 
IONAME(SetConvert)684 bool IONAME(SetConvert)(
685     Cookie cookie, const char *keyword, std::size_t length) {
686   IoStatementState &io{*cookie};
687   auto *open{io.get_if<OpenStatementState>()};
688   if (!open) {
689     io.GetIoErrorHandler().Crash(
690         "SetConvert() called when not in an OPEN statement");
691   }
692   if (auto convert{GetConvertFromString(keyword, length)}) {
693     open->set_convert(*convert);
694     return true;
695   } else {
696     open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'",
697         static_cast<int>(length), keyword);
698     return false;
699   }
700 }
701 
IONAME(SetEncoding)702 bool IONAME(SetEncoding)(
703     Cookie cookie, const char *keyword, std::size_t length) {
704   IoStatementState &io{*cookie};
705   auto *open{io.get_if<OpenStatementState>()};
706   if (!open) {
707     io.GetIoErrorHandler().Crash(
708         "SetEncoding() called when not in an OPEN statement");
709   }
710   bool isUTF8{false};
711   static const char *keywords[]{"UTF-8", "DEFAULT", nullptr};
712   switch (IdentifyValue(keyword, length, keywords)) {
713   case 0:
714     isUTF8 = true;
715     break;
716   case 1:
717     isUTF8 = false;
718     break;
719   default:
720     open->SignalError(IostatErrorInKeyword, "Invalid ENCODING='%.*s'",
721         static_cast<int>(length), keyword);
722   }
723   if (isUTF8 != open->unit().isUTF8) {
724     if (open->wasExtant()) {
725       open->SignalError("ENCODING= may not be changed on an open unit");
726     }
727     open->unit().isUTF8 = isUTF8;
728   }
729   return true;
730 }
731 
IONAME(SetForm)732 bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
733   IoStatementState &io{*cookie};
734   auto *open{io.get_if<OpenStatementState>()};
735   if (!open) {
736     io.GetIoErrorHandler().Crash(
737         "SetForm() called when not in an OPEN statement");
738   }
739   static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr};
740   switch (IdentifyValue(keyword, length, keywords)) {
741   case 0:
742     open->set_isUnformatted(false);
743     break;
744   case 1:
745     open->set_isUnformatted(true);
746     break;
747   default:
748     open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'",
749         static_cast<int>(length), keyword);
750   }
751   return true;
752 }
753 
IONAME(SetPosition)754 bool IONAME(SetPosition)(
755     Cookie cookie, const char *keyword, std::size_t length) {
756   IoStatementState &io{*cookie};
757   auto *open{io.get_if<OpenStatementState>()};
758   if (!open) {
759     io.GetIoErrorHandler().Crash(
760         "SetPosition() called when not in an OPEN statement");
761   }
762   static const char *positions[]{"ASIS", "REWIND", "APPEND", nullptr};
763   switch (IdentifyValue(keyword, length, positions)) {
764   case 0:
765     open->set_position(Position::AsIs);
766     return true;
767   case 1:
768     open->set_position(Position::Rewind);
769     return true;
770   case 2:
771     open->set_position(Position::Append);
772     return true;
773   default:
774     io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
775         "Invalid POSITION='%.*s'", static_cast<int>(length), keyword);
776   }
777   return true;
778 }
779 
IONAME(SetRecl)780 bool IONAME(SetRecl)(Cookie cookie, std::size_t n) {
781   IoStatementState &io{*cookie};
782   auto *open{io.get_if<OpenStatementState>()};
783   if (!open) {
784     io.GetIoErrorHandler().Crash(
785         "SetRecl() called when not in an OPEN statement");
786   }
787   if (n <= 0) {
788     io.GetIoErrorHandler().SignalError("RECL= must be greater than zero");
789   }
790   if (open->wasExtant() && open->unit().isFixedRecordLength &&
791       open->unit().recordLength.value_or(n) != static_cast<std::int64_t>(n)) {
792     open->SignalError("RECL= may not be changed for an open unit");
793   }
794   open->unit().isFixedRecordLength = true;
795   open->unit().recordLength = n;
796   return true;
797 }
798 
IONAME(SetStatus)799 bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) {
800   IoStatementState &io{*cookie};
801   if (auto *open{io.get_if<OpenStatementState>()}) {
802     static const char *statuses[]{
803         "OLD", "NEW", "SCRATCH", "REPLACE", "UNKNOWN", nullptr};
804     switch (IdentifyValue(keyword, length, statuses)) {
805     case 0:
806       open->set_status(OpenStatus::Old);
807       return true;
808     case 1:
809       open->set_status(OpenStatus::New);
810       return true;
811     case 2:
812       open->set_status(OpenStatus::Scratch);
813       return true;
814     case 3:
815       open->set_status(OpenStatus::Replace);
816       return true;
817     case 4:
818       open->set_status(OpenStatus::Unknown);
819       return true;
820     default:
821       io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
822           "Invalid STATUS='%.*s'", static_cast<int>(length), keyword);
823     }
824     return false;
825   }
826   if (auto *close{io.get_if<CloseStatementState>()}) {
827     static const char *statuses[]{"KEEP", "DELETE", nullptr};
828     switch (IdentifyValue(keyword, length, statuses)) {
829     case 0:
830       close->set_status(CloseStatus::Keep);
831       return true;
832     case 1:
833       close->set_status(CloseStatus::Delete);
834       return true;
835     default:
836       io.GetIoErrorHandler().SignalError(IostatErrorInKeyword,
837           "Invalid STATUS='%.*s'", static_cast<int>(length), keyword);
838     }
839     return false;
840   }
841   if (io.get_if<NoopCloseStatementState>()) {
842     return true; // don't bother validating STATUS= in a no-op CLOSE
843   }
844   io.GetIoErrorHandler().Crash(
845       "SetStatus() called when not in an OPEN or CLOSE statement");
846 }
847 
IONAME(SetFile)848 bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) {
849   IoStatementState &io{*cookie};
850   if (auto *open{io.get_if<OpenStatementState>()}) {
851     open->set_path(path, chars);
852     return true;
853   }
854   io.GetIoErrorHandler().Crash(
855       "SetFile() called when not in an OPEN statement");
856   return false;
857 }
858 
859 template <typename INT>
SetInteger(INT & x,int kind,std::int64_t value)860 static bool SetInteger(INT &x, int kind, std::int64_t value) {
861   switch (kind) {
862   case 1:
863     reinterpret_cast<std::int8_t &>(x) = value;
864     return true;
865   case 2:
866     reinterpret_cast<std::int16_t &>(x) = value;
867     return true;
868   case 4:
869     reinterpret_cast<std::int32_t &>(x) = value;
870     return true;
871   case 8:
872     reinterpret_cast<std::int64_t &>(x) = value;
873     return true;
874   default:
875     return false;
876   }
877 }
878 
IONAME(GetNewUnit)879 bool IONAME(GetNewUnit)(Cookie cookie, int &unit, int kind) {
880   IoStatementState &io{*cookie};
881   auto *open{io.get_if<OpenStatementState>()};
882   if (!open) {
883     io.GetIoErrorHandler().Crash(
884         "GetNewUnit() called when not in an OPEN statement");
885   }
886   if (!SetInteger(unit, kind, open->unit().unitNumber())) {
887     open->SignalError("GetNewUnit(): Bad INTEGER kind(%d) for result");
888   }
889   return true;
890 }
891 
892 // Data transfers
893 
IONAME(OutputDescriptor)894 bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
895   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
896 }
897 
IONAME(InputDescriptor)898 bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
899   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
900 }
901 
IONAME(OutputUnformattedBlock)902 bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x,
903     std::size_t length, std::size_t elementBytes) {
904   IoStatementState &io{*cookie};
905   if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Output>>()}) {
906     return unf->Emit(x, length, elementBytes);
907   }
908   io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O "
909                                "statement that is not unformatted output");
910   return false;
911 }
912 
IONAME(InputUnformattedBlock)913 bool IONAME(InputUnformattedBlock)(
914     Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) {
915   IoStatementState &io{*cookie};
916   io.BeginReadingRecord();
917   if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Input>>()}) {
918     return unf->Receive(x, length, elementBytes);
919   }
920   io.GetIoErrorHandler().Crash("InputUnformattedBlock() called for an I/O "
921                                "statement that is not unformatted output");
922   return false;
923 }
924 
IONAME(OutputInteger64)925 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) {
926   cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64");
927   StaticDescriptor staticDescriptor;
928   Descriptor &descriptor{staticDescriptor.descriptor()};
929   descriptor.Establish(
930       TypeCategory::Integer, sizeof n, reinterpret_cast<void *>(&n), 0);
931   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
932 }
933 
IONAME(InputInteger)934 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
935   cookie->CheckFormattedStmtType<Direction::Input>("InputInteger");
936   StaticDescriptor staticDescriptor;
937   Descriptor &descriptor{staticDescriptor.descriptor()};
938   descriptor.Establish(
939       TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0);
940   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
941 }
942 
IONAME(OutputReal32)943 bool IONAME(OutputReal32)(Cookie cookie, float x) {
944   cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32");
945   StaticDescriptor staticDescriptor;
946   Descriptor &descriptor{staticDescriptor.descriptor()};
947   descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
948   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
949 }
950 
IONAME(OutputReal64)951 bool IONAME(OutputReal64)(Cookie cookie, double x) {
952   cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64");
953   StaticDescriptor staticDescriptor;
954   Descriptor &descriptor{staticDescriptor.descriptor()};
955   descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
956   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
957 }
958 
IONAME(InputReal32)959 bool IONAME(InputReal32)(Cookie cookie, float &x) {
960   cookie->CheckFormattedStmtType<Direction::Input>("InputReal32");
961   StaticDescriptor staticDescriptor;
962   Descriptor &descriptor{staticDescriptor.descriptor()};
963   descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
964   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
965 }
966 
IONAME(InputReal64)967 bool IONAME(InputReal64)(Cookie cookie, double &x) {
968   cookie->CheckFormattedStmtType<Direction::Input>("InputReal64");
969   StaticDescriptor staticDescriptor;
970   Descriptor &descriptor{staticDescriptor.descriptor()};
971   descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
972   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
973 }
974 
IONAME(OutputComplex32)975 bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) {
976   cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32");
977   float z[2]{r, i};
978   StaticDescriptor staticDescriptor;
979   Descriptor &descriptor{staticDescriptor.descriptor()};
980   descriptor.Establish(
981       TypeCategory::Complex, 4, reinterpret_cast<void *>(&z), 0);
982   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
983 }
984 
IONAME(OutputComplex64)985 bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) {
986   cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64");
987   double z[2]{r, i};
988   StaticDescriptor staticDescriptor;
989   Descriptor &descriptor{staticDescriptor.descriptor()};
990   descriptor.Establish(
991       TypeCategory::Complex, 8, reinterpret_cast<void *>(&z), 0);
992   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
993 }
994 
IONAME(InputComplex32)995 bool IONAME(InputComplex32)(Cookie cookie, float z[2]) {
996   cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32");
997   StaticDescriptor staticDescriptor;
998   Descriptor &descriptor{staticDescriptor.descriptor()};
999   descriptor.Establish(
1000       TypeCategory::Complex, 4, reinterpret_cast<void *>(z), 0);
1001   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1002 }
1003 
IONAME(InputComplex64)1004 bool IONAME(InputComplex64)(Cookie cookie, double z[2]) {
1005   cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64");
1006   StaticDescriptor staticDescriptor;
1007   Descriptor &descriptor{staticDescriptor.descriptor()};
1008   descriptor.Establish(
1009       TypeCategory::Complex, 8, reinterpret_cast<void *>(z), 0);
1010   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1011 }
1012 
IONAME(OutputCharacter)1013 bool IONAME(OutputCharacter)(
1014     Cookie cookie, const char *x, std::size_t length, int kind) {
1015   cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter");
1016   StaticDescriptor staticDescriptor;
1017   Descriptor &descriptor{staticDescriptor.descriptor()};
1018   descriptor.Establish(
1019       kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
1020   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1021 }
1022 
IONAME(OutputAscii)1023 bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) {
1024   return IONAME(OutputCharacter(cookie, x, length, 1));
1025 }
1026 
IONAME(InputCharacter)1027 bool IONAME(InputCharacter)(
1028     Cookie cookie, char *x, std::size_t length, int kind) {
1029   cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter");
1030   StaticDescriptor staticDescriptor;
1031   Descriptor &descriptor{staticDescriptor.descriptor()};
1032   descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0);
1033   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1034 }
1035 
IONAME(InputAscii)1036 bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) {
1037   return IONAME(InputCharacter(cookie, x, length, 1));
1038 }
1039 
IONAME(OutputLogical)1040 bool IONAME(OutputLogical)(Cookie cookie, bool truth) {
1041   cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical");
1042   StaticDescriptor staticDescriptor;
1043   Descriptor &descriptor{staticDescriptor.descriptor()};
1044   descriptor.Establish(
1045       TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
1046   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
1047 }
1048 
IONAME(InputLogical)1049 bool IONAME(InputLogical)(Cookie cookie, bool &truth) {
1050   cookie->CheckFormattedStmtType<Direction::Input>("InputLogical");
1051   StaticDescriptor staticDescriptor;
1052   Descriptor &descriptor{staticDescriptor.descriptor()};
1053   descriptor.Establish(
1054       TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
1055   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
1056 }
1057 
IONAME(GetIoMsg)1058 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
1059   IoErrorHandler &handler{cookie->GetIoErrorHandler()};
1060   if (handler.GetIoStat()) { // leave "msg" alone when no error
1061     handler.GetIoMsg(msg, length);
1062   }
1063 }
1064 
IONAME(InquireCharacter)1065 bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry,
1066     char *result, std::size_t length) {
1067   IoStatementState &io{*cookie};
1068   return io.Inquire(inquiry, result, length);
1069 }
1070 
IONAME(InquireLogical)1071 bool IONAME(InquireLogical)(
1072     Cookie cookie, InquiryKeywordHash inquiry, bool &result) {
1073   IoStatementState &io{*cookie};
1074   return io.Inquire(inquiry, result);
1075 }
1076 
IONAME(InquirePendingId)1077 bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) {
1078   IoStatementState &io{*cookie};
1079   return io.Inquire(HashInquiryKeyword("PENDING"), id, result);
1080 }
1081 
IONAME(InquireInteger64)1082 bool IONAME(InquireInteger64)(
1083     Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) {
1084   IoStatementState &io{*cookie};
1085   std::int64_t n;
1086   if (io.Inquire(inquiry, n)) {
1087     SetInteger(result, kind, n);
1088     return true;
1089   }
1090   return false;
1091 }
1092 
IONAME(EndIoStatement)1093 enum Iostat IONAME(EndIoStatement)(Cookie cookie) {
1094   IoStatementState &io{*cookie};
1095   return static_cast<enum Iostat>(io.EndIoStatement());
1096 }
1097 } // namespace Fortran::runtime::io
1098