1 | //===-- runtime/io-stmt.h ---------------------------------------*- 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 | // Representations of the state of an I/O statement in progress |
10 | |
11 | #ifndef FORTRAN_RUNTIME_IO_STMT_H_ |
12 | #define FORTRAN_RUNTIME_IO_STMT_H_ |
13 | |
14 | #include "connection.h" |
15 | #include "file.h" |
16 | #include "format.h" |
17 | #include "internal-unit.h" |
18 | #include "io-error.h" |
19 | #include "flang/Common/visit.h" |
20 | #include "flang/Runtime/descriptor.h" |
21 | #include "flang/Runtime/io-api.h" |
22 | #include <functional> |
23 | #include <type_traits> |
24 | #include <variant> |
25 | |
26 | namespace Fortran::runtime::io { |
27 | |
28 | class ExternalFileUnit; |
29 | class ChildIo; |
30 | |
31 | class OpenStatementState; |
32 | class InquireUnitState; |
33 | class InquireNoUnitState; |
34 | class InquireUnconnectedFileState; |
35 | class InquireIOLengthState; |
36 | class ExternalMiscIoStatementState; |
37 | class CloseStatementState; |
38 | class NoopStatementState; // CLOSE or FLUSH on unknown unit |
39 | class ErroneousIoStatementState; |
40 | |
41 | template <Direction, typename CHAR = char> |
42 | class InternalFormattedIoStatementState; |
43 | template <Direction> class InternalListIoStatementState; |
44 | template <Direction, typename CHAR = char> |
45 | class ExternalFormattedIoStatementState; |
46 | template <Direction> class ExternalListIoStatementState; |
47 | template <Direction> class ExternalUnformattedIoStatementState; |
48 | template <Direction, typename CHAR = char> class ChildFormattedIoStatementState; |
49 | template <Direction> class ChildListIoStatementState; |
50 | template <Direction> class ChildUnformattedIoStatementState; |
51 | |
52 | struct InputStatementState {}; |
53 | struct OutputStatementState {}; |
54 | template <Direction D> |
55 | using IoDirectionState = std::conditional_t<D == Direction::Input, |
56 | InputStatementState, OutputStatementState>; |
57 | |
58 | // Common state for all kinds of formatted I/O |
59 | template <Direction D> class FormattedIoStatementState {}; |
60 | template <> class FormattedIoStatementState<Direction::Input> { |
61 | public: |
62 | std::size_t GetEditDescriptorChars() const; |
63 | void GotChar(int); |
64 | |
65 | private: |
66 | // Account of characters read for edit descriptors (i.e., formatted I/O |
67 | // with a FORMAT, not list-directed or NAMELIST), not including padding. |
68 | std::size_t chars_{0}; // for READ(SIZE=) |
69 | }; |
70 | |
71 | // The Cookie type in the I/O API is a pointer (for C) to this class. |
72 | class IoStatementState { |
73 | public: |
74 | template <typename A> explicit IoStatementState(A &x) : u_{x} {} |
75 | |
76 | // These member functions each project themselves into the active alternative. |
77 | // They're used by per-data-item routines in the I/O API (e.g., OutputReal64) |
78 | // to interact with the state of the I/O statement in progress. |
79 | // This design avoids virtual member functions and function pointers, |
80 | // which may not have good support in some runtime environments. |
81 | |
82 | // CompleteOperation() is the last opportunity to raise an I/O error. |
83 | // It is called by EndIoStatement(), but it can be invoked earlier to |
84 | // catch errors for (e.g.) GetIoMsg() and GetNewUnit(). If called |
85 | // more than once, it is a no-op. |
86 | void CompleteOperation(); |
87 | // Completes an I/O statement and reclaims storage. |
88 | int EndIoStatement(); |
89 | |
90 | bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); |
91 | bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
92 | std::size_t GetNextInputBytes(const char *&); |
93 | bool AdvanceRecord(int = 1); |
94 | void BackspaceRecord(); |
95 | void HandleRelativePosition(std::int64_t byteOffset); |
96 | void HandleAbsolutePosition(std::int64_t byteOffset); // for r* in list I/O |
97 | std::optional<DataEdit> GetNextDataEdit(int maxRepeat = 1); |
98 | ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit |
99 | bool BeginReadingRecord(); |
100 | void FinishReadingRecord(); |
101 | bool Inquire(InquiryKeywordHash, char *, std::size_t); |
102 | bool Inquire(InquiryKeywordHash, bool &); |
103 | bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING= |
104 | bool Inquire(InquiryKeywordHash, std::int64_t &); |
105 | std::int64_t InquirePos(); |
106 | void GotChar(signed int = 1); // for READ(SIZE=); can be <0 |
107 | |
108 | MutableModes &mutableModes(); |
109 | ConnectionState &GetConnectionState(); |
110 | IoErrorHandler &GetIoErrorHandler() const; |
111 | |
112 | // N.B.: this also works with base classes |
113 | template <typename A> A *get_if() const { |
114 | return common::visit( |
115 | [](auto &x) -> A * { |
116 | if constexpr (std::is_convertible_v<decltype(x.get()), A &>) { |
117 | return &x.get(); |
118 | } |
119 | return nullptr; |
120 | }, |
121 | u_); |
122 | } |
123 | |
124 | // Vacant after the end of the current record |
125 | std::optional<char32_t> GetCurrentChar(std::size_t &byteCount); |
126 | |
127 | // The "remaining" arguments to CueUpInput(), SkipSpaces(), & NextInField() |
128 | // are always in units of bytes, not characters; the distinction matters |
129 | // for internal input from CHARACTER(KIND=2 and 4). |
130 | |
131 | // For fixed-width fields, return the number of remaining bytes. |
132 | // Skip over leading blanks. |
133 | std::optional<int> CueUpInput(const DataEdit &edit) { |
134 | std::optional<int> remaining; |
135 | if (edit.IsListDirected()) { |
136 | std::size_t byteCount{0}; |
137 | GetNextNonBlank(byteCount); |
138 | } else { |
139 | if (edit.width.value_or(u: 0) > 0) { |
140 | remaining = *edit.width; |
141 | if (int bytesPerChar{GetConnectionState().internalIoCharKind}; |
142 | bytesPerChar > 1) { |
143 | *remaining *= bytesPerChar; |
144 | } |
145 | } |
146 | SkipSpaces(remaining); |
147 | } |
148 | return remaining; |
149 | } |
150 | |
151 | std::optional<char32_t> SkipSpaces(std::optional<int> &remaining) { |
152 | while (!remaining || *remaining > 0) { |
153 | std::size_t byteCount{0}; |
154 | if (auto ch{GetCurrentChar(byteCount)}) { |
155 | if (*ch != ' ' && *ch != '\t') { |
156 | return ch; |
157 | } |
158 | if (remaining) { |
159 | if (static_cast<std::size_t>(*remaining) < byteCount) { |
160 | break; |
161 | } |
162 | GotChar(byteCount); |
163 | *remaining -= byteCount; |
164 | } |
165 | HandleRelativePosition(byteOffset: byteCount); |
166 | } else { |
167 | break; |
168 | } |
169 | } |
170 | return std::nullopt; |
171 | } |
172 | |
173 | // Acquires the next input character, respecting any applicable field width |
174 | // or separator character. |
175 | std::optional<char32_t> NextInField( |
176 | std::optional<int> &remaining, const DataEdit &); |
177 | |
178 | // Detect and signal any end-of-record condition after input. |
179 | // Returns true if at EOR and remaining input should be padded with blanks. |
180 | bool CheckForEndOfRecord(std::size_t afterReading); |
181 | |
182 | // Skips spaces, advances records, and ignores NAMELIST comments |
183 | std::optional<char32_t> GetNextNonBlank(std::size_t &byteCount) { |
184 | auto ch{GetCurrentChar(byteCount)}; |
185 | bool inNamelist{mutableModes().inNamelist}; |
186 | while (!ch || *ch == ' ' || *ch == '\t' || (inNamelist && *ch == '!')) { |
187 | if (ch && (*ch == ' ' || *ch == '\t')) { |
188 | HandleRelativePosition(byteOffset: byteCount); |
189 | } else if (!AdvanceRecord()) { |
190 | return std::nullopt; |
191 | } |
192 | ch = GetCurrentChar(byteCount); |
193 | } |
194 | return ch; |
195 | } |
196 | |
197 | template <Direction D> bool CheckFormattedStmtType(const char *name) { |
198 | if (get_if<FormattedIoStatementState<D>>()) { |
199 | return true; |
200 | } else { |
201 | auto &handler{GetIoErrorHandler()}; |
202 | if (!handler.InError()) { |
203 | handler.Crash("%s called for I/O statement that is not formatted %s" , |
204 | name, D == Direction::Output ? "output" : "input" ); |
205 | } |
206 | return false; |
207 | } |
208 | } |
209 | |
210 | private: |
211 | std::variant<std::reference_wrapper<OpenStatementState>, |
212 | std::reference_wrapper<CloseStatementState>, |
213 | std::reference_wrapper<NoopStatementState>, |
214 | std::reference_wrapper< |
215 | InternalFormattedIoStatementState<Direction::Output>>, |
216 | std::reference_wrapper< |
217 | InternalFormattedIoStatementState<Direction::Input>>, |
218 | std::reference_wrapper<InternalListIoStatementState<Direction::Output>>, |
219 | std::reference_wrapper<InternalListIoStatementState<Direction::Input>>, |
220 | std::reference_wrapper< |
221 | ExternalFormattedIoStatementState<Direction::Output>>, |
222 | std::reference_wrapper< |
223 | ExternalFormattedIoStatementState<Direction::Input>>, |
224 | std::reference_wrapper<ExternalListIoStatementState<Direction::Output>>, |
225 | std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>, |
226 | std::reference_wrapper< |
227 | ExternalUnformattedIoStatementState<Direction::Output>>, |
228 | std::reference_wrapper< |
229 | ExternalUnformattedIoStatementState<Direction::Input>>, |
230 | std::reference_wrapper<ChildFormattedIoStatementState<Direction::Output>>, |
231 | std::reference_wrapper<ChildFormattedIoStatementState<Direction::Input>>, |
232 | std::reference_wrapper<ChildListIoStatementState<Direction::Output>>, |
233 | std::reference_wrapper<ChildListIoStatementState<Direction::Input>>, |
234 | std::reference_wrapper< |
235 | ChildUnformattedIoStatementState<Direction::Output>>, |
236 | std::reference_wrapper< |
237 | ChildUnformattedIoStatementState<Direction::Input>>, |
238 | std::reference_wrapper<InquireUnitState>, |
239 | std::reference_wrapper<InquireNoUnitState>, |
240 | std::reference_wrapper<InquireUnconnectedFileState>, |
241 | std::reference_wrapper<InquireIOLengthState>, |
242 | std::reference_wrapper<ExternalMiscIoStatementState>, |
243 | std::reference_wrapper<ErroneousIoStatementState>> |
244 | u_; |
245 | }; |
246 | |
247 | // Base class for all per-I/O statement state classes. |
248 | class IoStatementBase : public IoErrorHandler { |
249 | public: |
250 | using IoErrorHandler::IoErrorHandler; |
251 | |
252 | bool completedOperation() const { return completedOperation_; } |
253 | |
254 | void CompleteOperation() { completedOperation_ = true; } |
255 | int EndIoStatement() { return GetIoStat(); } |
256 | |
257 | // These are default no-op backstops that can be overridden by descendants. |
258 | bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); |
259 | bool Receive(char *, std::size_t bytes, std::size_t elementBytes = 0); |
260 | std::size_t GetNextInputBytes(const char *&); |
261 | bool AdvanceRecord(int); |
262 | void BackspaceRecord(); |
263 | void HandleRelativePosition(std::int64_t); |
264 | void HandleAbsolutePosition(std::int64_t); |
265 | std::optional<DataEdit> GetNextDataEdit( |
266 | IoStatementState &, int maxRepeat = 1); |
267 | ExternalFileUnit *GetExternalFileUnit() const; |
268 | bool BeginReadingRecord(); |
269 | void FinishReadingRecord(); |
270 | bool Inquire(InquiryKeywordHash, char *, std::size_t); |
271 | bool Inquire(InquiryKeywordHash, bool &); |
272 | bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
273 | bool Inquire(InquiryKeywordHash, std::int64_t &); |
274 | std::int64_t InquirePos(); |
275 | |
276 | void BadInquiryKeywordHashCrash(InquiryKeywordHash); |
277 | |
278 | protected: |
279 | bool completedOperation_{false}; |
280 | }; |
281 | |
282 | // Common state for list-directed & NAMELIST I/O, both internal & external |
283 | template <Direction> class ListDirectedStatementState; |
284 | template <> |
285 | class ListDirectedStatementState<Direction::Output> |
286 | : public FormattedIoStatementState<Direction::Output> { |
287 | public: |
288 | bool EmitLeadingSpaceOrAdvance( |
289 | IoStatementState &, std::size_t = 1, bool isCharacter = false); |
290 | std::optional<DataEdit> GetNextDataEdit( |
291 | IoStatementState &, int maxRepeat = 1); |
292 | bool lastWasUndelimitedCharacter() const { |
293 | return lastWasUndelimitedCharacter_; |
294 | } |
295 | void set_lastWasUndelimitedCharacter(bool yes = true) { |
296 | lastWasUndelimitedCharacter_ = yes; |
297 | } |
298 | |
299 | private: |
300 | bool lastWasUndelimitedCharacter_{false}; |
301 | }; |
302 | template <> |
303 | class ListDirectedStatementState<Direction::Input> |
304 | : public FormattedIoStatementState<Direction::Input> { |
305 | public: |
306 | bool inNamelistSequence() const { return inNamelistSequence_; } |
307 | int EndIoStatement(); |
308 | |
309 | // Skips value separators, handles repetition and null values. |
310 | // Vacant when '/' appears; present with descriptor == ListDirectedNullValue |
311 | // when a null value appears. |
312 | std::optional<DataEdit> GetNextDataEdit( |
313 | IoStatementState &, int maxRepeat = 1); |
314 | |
315 | // Each NAMELIST input item is treated like a distinct list-directed |
316 | // input statement. This member function resets some state so that |
317 | // repetition and null values work correctly for each successive |
318 | // NAMELIST input item. |
319 | void ResetForNextNamelistItem(bool inNamelistSequence) { |
320 | remaining_ = 0; |
321 | if (repeatPosition_) { |
322 | repeatPosition_->Cancel(); |
323 | } |
324 | eatComma_ = false; |
325 | realPart_ = imaginaryPart_ = false; |
326 | inNamelistSequence_ = inNamelistSequence; |
327 | } |
328 | |
329 | private: |
330 | int remaining_{0}; // for "r*" repetition |
331 | std::optional<SavedPosition> repeatPosition_; |
332 | bool eatComma_{false}; // consume comma after previously read item |
333 | bool hitSlash_{false}; // once '/' is seen, nullify further items |
334 | bool realPart_{false}; |
335 | bool imaginaryPart_{false}; |
336 | bool inNamelistSequence_{false}; |
337 | }; |
338 | |
339 | template <Direction DIR> |
340 | class InternalIoStatementState : public IoStatementBase, |
341 | public IoDirectionState<DIR> { |
342 | public: |
343 | using Buffer = |
344 | std::conditional_t<DIR == Direction::Input, const char *, char *>; |
345 | InternalIoStatementState(Buffer, std::size_t, |
346 | const char *sourceFile = nullptr, int sourceLine = 0); |
347 | InternalIoStatementState( |
348 | const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); |
349 | int EndIoStatement(); |
350 | |
351 | bool Emit(const char *data, std::size_t bytes, std::size_t elementBytes = 0); |
352 | std::size_t GetNextInputBytes(const char *&); |
353 | bool AdvanceRecord(int = 1); |
354 | void BackspaceRecord(); |
355 | ConnectionState &GetConnectionState() { return unit_; } |
356 | MutableModes &mutableModes() { return unit_.modes; } |
357 | void HandleRelativePosition(std::int64_t); |
358 | void HandleAbsolutePosition(std::int64_t); |
359 | std::int64_t InquirePos(); |
360 | |
361 | protected: |
362 | bool free_{true}; |
363 | InternalDescriptorUnit<DIR> unit_; |
364 | }; |
365 | |
366 | template <Direction DIR, typename CHAR> |
367 | class InternalFormattedIoStatementState |
368 | : public InternalIoStatementState<DIR>, |
369 | public FormattedIoStatementState<DIR> { |
370 | public: |
371 | using CharType = CHAR; |
372 | using typename InternalIoStatementState<DIR>::Buffer; |
373 | InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength, |
374 | const CharType *format, std::size_t formatLength, |
375 | const Descriptor *formatDescriptor = nullptr, |
376 | const char *sourceFile = nullptr, int sourceLine = 0); |
377 | InternalFormattedIoStatementState(const Descriptor &, const CharType *format, |
378 | std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
379 | const char *sourceFile = nullptr, int sourceLine = 0); |
380 | IoStatementState &ioStatementState() { return ioStatementState_; } |
381 | void CompleteOperation(); |
382 | int EndIoStatement(); |
383 | std::optional<DataEdit> GetNextDataEdit( |
384 | IoStatementState &, int maxRepeat = 1) { |
385 | return format_.GetNextDataEdit(*this, maxRepeat); |
386 | } |
387 | |
388 | private: |
389 | IoStatementState ioStatementState_; // points to *this |
390 | using InternalIoStatementState<DIR>::unit_; |
391 | // format_ *must* be last; it may be partial someday |
392 | FormatControl<InternalFormattedIoStatementState> format_; |
393 | }; |
394 | |
395 | template <Direction DIR> |
396 | class InternalListIoStatementState : public InternalIoStatementState<DIR>, |
397 | public ListDirectedStatementState<DIR> { |
398 | public: |
399 | using typename InternalIoStatementState<DIR>::Buffer; |
400 | InternalListIoStatementState(Buffer internal, std::size_t internalLength, |
401 | const char *sourceFile = nullptr, int sourceLine = 0); |
402 | InternalListIoStatementState( |
403 | const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0); |
404 | IoStatementState &ioStatementState() { return ioStatementState_; } |
405 | using ListDirectedStatementState<DIR>::GetNextDataEdit; |
406 | int EndIoStatement(); |
407 | |
408 | private: |
409 | IoStatementState ioStatementState_; // points to *this |
410 | using InternalIoStatementState<DIR>::unit_; |
411 | }; |
412 | |
413 | class ExternalIoStatementBase : public IoStatementBase { |
414 | public: |
415 | ExternalIoStatementBase( |
416 | ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); |
417 | ExternalFileUnit &unit() { return unit_; } |
418 | MutableModes &mutableModes(); |
419 | ConnectionState &GetConnectionState(); |
420 | int asynchronousID() const { return asynchronousID_; } |
421 | int EndIoStatement(); |
422 | ExternalFileUnit *GetExternalFileUnit() const { return &unit_; } |
423 | void SetAsynchronous(); |
424 | std::int64_t InquirePos(); |
425 | |
426 | private: |
427 | ExternalFileUnit &unit_; |
428 | int asynchronousID_{-1}; |
429 | }; |
430 | |
431 | template <Direction DIR> |
432 | class ExternalIoStatementState : public ExternalIoStatementBase, |
433 | public IoDirectionState<DIR> { |
434 | public: |
435 | ExternalIoStatementState( |
436 | ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); |
437 | MutableModes &mutableModes() { return mutableModes_; } |
438 | void CompleteOperation(); |
439 | int EndIoStatement(); |
440 | bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); |
441 | std::size_t GetNextInputBytes(const char *&); |
442 | bool AdvanceRecord(int = 1); |
443 | void BackspaceRecord(); |
444 | void HandleRelativePosition(std::int64_t); |
445 | void HandleAbsolutePosition(std::int64_t); |
446 | bool BeginReadingRecord(); |
447 | void FinishReadingRecord(); |
448 | |
449 | private: |
450 | // These are forked from ConnectionState's modes at the beginning |
451 | // of each formatted I/O statement so they may be overridden by control |
452 | // edit descriptors during the statement. |
453 | MutableModes mutableModes_; |
454 | }; |
455 | |
456 | template <Direction DIR, typename CHAR> |
457 | class ExternalFormattedIoStatementState |
458 | : public ExternalIoStatementState<DIR>, |
459 | public FormattedIoStatementState<DIR> { |
460 | public: |
461 | using CharType = CHAR; |
462 | ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, |
463 | std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
464 | const char *sourceFile = nullptr, int sourceLine = 0); |
465 | void CompleteOperation(); |
466 | int EndIoStatement(); |
467 | std::optional<DataEdit> GetNextDataEdit( |
468 | IoStatementState &, int maxRepeat = 1) { |
469 | return format_.GetNextDataEdit(*this, maxRepeat); |
470 | } |
471 | |
472 | private: |
473 | FormatControl<ExternalFormattedIoStatementState> format_; |
474 | }; |
475 | |
476 | template <Direction DIR> |
477 | class ExternalListIoStatementState : public ExternalIoStatementState<DIR>, |
478 | public ListDirectedStatementState<DIR> { |
479 | public: |
480 | using ExternalIoStatementState<DIR>::ExternalIoStatementState; |
481 | using ListDirectedStatementState<DIR>::GetNextDataEdit; |
482 | int EndIoStatement(); |
483 | }; |
484 | |
485 | template <Direction DIR> |
486 | class ExternalUnformattedIoStatementState |
487 | : public ExternalIoStatementState<DIR> { |
488 | public: |
489 | using ExternalIoStatementState<DIR>::ExternalIoStatementState; |
490 | bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
491 | }; |
492 | |
493 | template <Direction DIR> |
494 | class ChildIoStatementState : public IoStatementBase, |
495 | public IoDirectionState<DIR> { |
496 | public: |
497 | ChildIoStatementState( |
498 | ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0); |
499 | ChildIo &child() { return child_; } |
500 | MutableModes &mutableModes(); |
501 | ConnectionState &GetConnectionState(); |
502 | ExternalFileUnit *GetExternalFileUnit() const; |
503 | int EndIoStatement(); |
504 | bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); |
505 | std::size_t GetNextInputBytes(const char *&); |
506 | void HandleRelativePosition(std::int64_t); |
507 | void HandleAbsolutePosition(std::int64_t); |
508 | |
509 | private: |
510 | ChildIo &child_; |
511 | }; |
512 | |
513 | template <Direction DIR, typename CHAR> |
514 | class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>, |
515 | public FormattedIoStatementState<DIR> { |
516 | public: |
517 | using CharType = CHAR; |
518 | ChildFormattedIoStatementState(ChildIo &, const CharType *format, |
519 | std::size_t formatLength, const Descriptor *formatDescriptor = nullptr, |
520 | const char *sourceFile = nullptr, int sourceLine = 0); |
521 | MutableModes &mutableModes() { return mutableModes_; } |
522 | void CompleteOperation(); |
523 | int EndIoStatement(); |
524 | bool AdvanceRecord(int = 1); |
525 | std::optional<DataEdit> GetNextDataEdit( |
526 | IoStatementState &, int maxRepeat = 1) { |
527 | return format_.GetNextDataEdit(*this, maxRepeat); |
528 | } |
529 | |
530 | private: |
531 | MutableModes mutableModes_; |
532 | FormatControl<ChildFormattedIoStatementState> format_; |
533 | }; |
534 | |
535 | template <Direction DIR> |
536 | class ChildListIoStatementState : public ChildIoStatementState<DIR>, |
537 | public ListDirectedStatementState<DIR> { |
538 | public: |
539 | using ChildIoStatementState<DIR>::ChildIoStatementState; |
540 | using ListDirectedStatementState<DIR>::GetNextDataEdit; |
541 | int EndIoStatement(); |
542 | }; |
543 | |
544 | template <Direction DIR> |
545 | class ChildUnformattedIoStatementState : public ChildIoStatementState<DIR> { |
546 | public: |
547 | using ChildIoStatementState<DIR>::ChildIoStatementState; |
548 | bool Receive(char *, std::size_t, std::size_t elementBytes = 0); |
549 | }; |
550 | |
551 | // OPEN |
552 | class OpenStatementState : public ExternalIoStatementBase { |
553 | public: |
554 | OpenStatementState(ExternalFileUnit &unit, bool wasExtant, bool isNewUnit, |
555 | const char *sourceFile = nullptr, int sourceLine = 0) |
556 | : ExternalIoStatementBase{unit, sourceFile, sourceLine}, |
557 | wasExtant_{wasExtant}, isNewUnit_{isNewUnit} {} |
558 | bool wasExtant() const { return wasExtant_; } |
559 | void set_status(OpenStatus status) { status_ = status; } // STATUS= |
560 | void set_path(const char *, std::size_t); // FILE= |
561 | void set_position(Position position) { position_ = position; } // POSITION= |
562 | void set_action(Action action) { action_ = action; } // ACTION= |
563 | void set_convert(Convert convert) { convert_ = convert; } // CONVERT= |
564 | void set_access(Access access) { access_ = access; } // ACCESS= |
565 | void set_isUnformatted(bool yes = true) { isUnformatted_ = yes; } // FORM= |
566 | |
567 | void CompleteOperation(); |
568 | int EndIoStatement(); |
569 | |
570 | private: |
571 | bool wasExtant_; |
572 | bool isNewUnit_; |
573 | std::optional<OpenStatus> status_; |
574 | std::optional<Position> position_; |
575 | std::optional<Action> action_; |
576 | Convert convert_{Convert::Unknown}; |
577 | OwningPtr<char> path_; |
578 | std::size_t pathLength_; |
579 | std::optional<bool> isUnformatted_; |
580 | std::optional<Access> access_; |
581 | }; |
582 | |
583 | class CloseStatementState : public ExternalIoStatementBase { |
584 | public: |
585 | CloseStatementState(ExternalFileUnit &unit, const char *sourceFile = nullptr, |
586 | int sourceLine = 0) |
587 | : ExternalIoStatementBase{unit, sourceFile, sourceLine} {} |
588 | void set_status(CloseStatus status) { status_ = status; } |
589 | int EndIoStatement(); |
590 | |
591 | private: |
592 | CloseStatus status_{CloseStatus::Keep}; |
593 | }; |
594 | |
595 | // For CLOSE(bad unit), WAIT(bad unit, ID=nonzero), INQUIRE(unconnected unit), |
596 | // and recoverable BACKSPACE(bad unit) |
597 | class NoUnitIoStatementState : public IoStatementBase { |
598 | public: |
599 | IoStatementState &ioStatementState() { return ioStatementState_; } |
600 | MutableModes &mutableModes() { return connection_.modes; } |
601 | ConnectionState &GetConnectionState() { return connection_; } |
602 | int badUnitNumber() const { return badUnitNumber_; } |
603 | void CompleteOperation(); |
604 | int EndIoStatement(); |
605 | |
606 | protected: |
607 | template <typename A> |
608 | NoUnitIoStatementState(A &stmt, const char *sourceFile = nullptr, |
609 | int sourceLine = 0, int badUnitNumber = -1) |
610 | : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt}, |
611 | badUnitNumber_{badUnitNumber} {} |
612 | |
613 | private: |
614 | IoStatementState ioStatementState_; // points to *this |
615 | ConnectionState connection_; |
616 | int badUnitNumber_; |
617 | }; |
618 | |
619 | class NoopStatementState : public NoUnitIoStatementState { |
620 | public: |
621 | NoopStatementState( |
622 | const char *sourceFile = nullptr, int sourceLine = 0, int unitNumber = -1) |
623 | : NoUnitIoStatementState{*this, sourceFile, sourceLine, unitNumber} {} |
624 | void set_status(CloseStatus) {} // discards |
625 | }; |
626 | |
627 | extern template class InternalIoStatementState<Direction::Output>; |
628 | extern template class InternalIoStatementState<Direction::Input>; |
629 | extern template class InternalFormattedIoStatementState<Direction::Output>; |
630 | extern template class InternalFormattedIoStatementState<Direction::Input>; |
631 | extern template class InternalListIoStatementState<Direction::Output>; |
632 | extern template class InternalListIoStatementState<Direction::Input>; |
633 | extern template class ExternalIoStatementState<Direction::Output>; |
634 | extern template class ExternalIoStatementState<Direction::Input>; |
635 | extern template class ExternalFormattedIoStatementState<Direction::Output>; |
636 | extern template class ExternalFormattedIoStatementState<Direction::Input>; |
637 | extern template class ExternalListIoStatementState<Direction::Output>; |
638 | extern template class ExternalListIoStatementState<Direction::Input>; |
639 | extern template class ExternalUnformattedIoStatementState<Direction::Output>; |
640 | extern template class ExternalUnformattedIoStatementState<Direction::Input>; |
641 | extern template class ChildIoStatementState<Direction::Output>; |
642 | extern template class ChildIoStatementState<Direction::Input>; |
643 | extern template class ChildFormattedIoStatementState<Direction::Output>; |
644 | extern template class ChildFormattedIoStatementState<Direction::Input>; |
645 | extern template class ChildListIoStatementState<Direction::Output>; |
646 | extern template class ChildListIoStatementState<Direction::Input>; |
647 | extern template class ChildUnformattedIoStatementState<Direction::Output>; |
648 | extern template class ChildUnformattedIoStatementState<Direction::Input>; |
649 | |
650 | extern template class FormatControl< |
651 | InternalFormattedIoStatementState<Direction::Output>>; |
652 | extern template class FormatControl< |
653 | InternalFormattedIoStatementState<Direction::Input>>; |
654 | extern template class FormatControl< |
655 | ExternalFormattedIoStatementState<Direction::Output>>; |
656 | extern template class FormatControl< |
657 | ExternalFormattedIoStatementState<Direction::Input>>; |
658 | extern template class FormatControl< |
659 | ChildFormattedIoStatementState<Direction::Output>>; |
660 | extern template class FormatControl< |
661 | ChildFormattedIoStatementState<Direction::Input>>; |
662 | |
663 | class InquireUnitState : public ExternalIoStatementBase { |
664 | public: |
665 | InquireUnitState(ExternalFileUnit &unit, const char *sourceFile = nullptr, |
666 | int sourceLine = 0); |
667 | bool Inquire(InquiryKeywordHash, char *, std::size_t); |
668 | bool Inquire(InquiryKeywordHash, bool &); |
669 | bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
670 | bool Inquire(InquiryKeywordHash, std::int64_t &); |
671 | }; |
672 | |
673 | class InquireNoUnitState : public NoUnitIoStatementState { |
674 | public: |
675 | InquireNoUnitState(const char *sourceFile = nullptr, int sourceLine = 0, |
676 | int badUnitNumber = -1); |
677 | bool Inquire(InquiryKeywordHash, char *, std::size_t); |
678 | bool Inquire(InquiryKeywordHash, bool &); |
679 | bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
680 | bool Inquire(InquiryKeywordHash, std::int64_t &); |
681 | }; |
682 | |
683 | class InquireUnconnectedFileState : public NoUnitIoStatementState { |
684 | public: |
685 | InquireUnconnectedFileState(OwningPtr<char> &&path, |
686 | const char *sourceFile = nullptr, int sourceLine = 0); |
687 | bool Inquire(InquiryKeywordHash, char *, std::size_t); |
688 | bool Inquire(InquiryKeywordHash, bool &); |
689 | bool Inquire(InquiryKeywordHash, std::int64_t, bool &); |
690 | bool Inquire(InquiryKeywordHash, std::int64_t &); |
691 | |
692 | private: |
693 | OwningPtr<char> path_; // trimmed and NUL terminated |
694 | }; |
695 | |
696 | class InquireIOLengthState : public NoUnitIoStatementState, |
697 | public OutputStatementState { |
698 | public: |
699 | InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0); |
700 | std::size_t bytes() const { return bytes_; } |
701 | bool Emit(const char *, std::size_t bytes, std::size_t elementBytes = 0); |
702 | |
703 | private: |
704 | std::size_t bytes_{0}; |
705 | }; |
706 | |
707 | class ExternalMiscIoStatementState : public ExternalIoStatementBase { |
708 | public: |
709 | enum Which { Flush, Backspace, Endfile, Rewind, Wait }; |
710 | ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which, |
711 | const char *sourceFile = nullptr, int sourceLine = 0) |
712 | : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {} |
713 | void CompleteOperation(); |
714 | int EndIoStatement(); |
715 | |
716 | private: |
717 | Which which_; |
718 | }; |
719 | |
720 | class ErroneousIoStatementState : public IoStatementBase { |
721 | public: |
722 | explicit ErroneousIoStatementState(Iostat iostat, |
723 | ExternalFileUnit *unit = nullptr, const char *sourceFile = nullptr, |
724 | int sourceLine = 0) |
725 | : IoStatementBase{sourceFile, sourceLine}, unit_{unit} { |
726 | SetPendingError(iostat); |
727 | } |
728 | int EndIoStatement(); |
729 | ConnectionState &GetConnectionState() { return connection_; } |
730 | MutableModes &mutableModes() { return connection_.modes; } |
731 | |
732 | private: |
733 | ConnectionState connection_; |
734 | ExternalFileUnit *unit_{nullptr}; |
735 | }; |
736 | |
737 | } // namespace Fortran::runtime::io |
738 | #endif // FORTRAN_RUNTIME_IO_STMT_H_ |
739 | |