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