blob: 8170fbc696c213b14bec531e7bf6c7e41c1cd70f [file] [log] [blame]
peter klauslerf7be2512020-01-23 16:59:27 -08001//===-- runtime/unit.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#include "unit.h"
peter klausler8f2c5c42020-07-21 17:37:35 -070010#include "environment.h"
peter klausler3b635712020-02-13 14:41:56 -080011#include "io-error.h"
peter klauslerf7be2512020-01-23 16:59:27 -080012#include "lock.h"
peter klausler3b635712020-02-13 14:41:56 -080013#include "unit-map.h"
peter klauslerbd43fa22020-07-14 11:28:03 -070014#include <cstdio>
peter klausler8f2c5c42020-07-21 17:37:35 -070015#include <utility>
peter klauslerf7be2512020-01-23 16:59:27 -080016
17namespace Fortran::runtime::io {
18
peter klausler3b635712020-02-13 14:41:56 -080019// The per-unit data structures are created on demand so that Fortran I/O
20// should work without a Fortran main program.
21static Lock unitMapLock;
22static UnitMap *unitMap{nullptr};
peter klausler00063542020-07-03 12:38:22 -070023static ExternalFileUnit *defaultInput{nullptr};
peter klausler95696d52020-02-04 16:55:45 -080024static ExternalFileUnit *defaultOutput{nullptr};
peter klauslerf7be2512020-01-23 16:59:27 -080025
peter klausler95696d52020-02-04 16:55:45 -080026void FlushOutputOnCrash(const Terminator &terminator) {
peter klausler3b635712020-02-13 14:41:56 -080027 if (!defaultOutput) {
28 return;
29 }
30 CriticalSection critical{unitMapLock};
peter klausler95696d52020-02-04 16:55:45 -080031 if (defaultOutput) {
32 IoErrorHandler handler{terminator};
Tim Keith1f879002020-03-28 21:00:16 -070033 handler.HasIoStat(); // prevent nested crash if flush has error
peter klausler95696d52020-02-04 16:55:45 -080034 defaultOutput->Flush(handler);
35 }
36}
37
38ExternalFileUnit *ExternalFileUnit::LookUp(int unit) {
peter klausler3b635712020-02-13 14:41:56 -080039 return GetUnitMap().LookUp(unit);
peter klauslerf7be2512020-01-23 16:59:27 -080040}
41
peter klausler95696d52020-02-04 16:55:45 -080042ExternalFileUnit &ExternalFileUnit::LookUpOrCrash(
43 int unit, const Terminator &terminator) {
peter klausler95696d52020-02-04 16:55:45 -080044 ExternalFileUnit *file{LookUp(unit)};
peter klauslerf7be2512020-01-23 16:59:27 -080045 if (!file) {
46 terminator.Crash("Not an open I/O unit number: %d", unit);
47 }
48 return *file;
49}
50
peter klausler3b635712020-02-13 14:41:56 -080051ExternalFileUnit &ExternalFileUnit::LookUpOrCreate(
peter klauslerbd43fa22020-07-14 11:28:03 -070052 int unit, const Terminator &terminator, bool &wasExtant) {
peter klausler3b635712020-02-13 14:41:56 -080053 return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant);
peter klauslerf7be2512020-01-23 16:59:27 -080054}
55
peter klauslerbd43fa22020-07-14 11:28:03 -070056ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
57 int unit, Direction dir, bool isUnformatted, const Terminator &terminator) {
58 bool exists{false};
59 ExternalFileUnit &result{
60 GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
61 if (!exists) {
peter klauslerbd43fa22020-07-14 11:28:03 -070062 IoErrorHandler handler{terminator};
peter klausler675ad1b2020-08-03 11:35:29 -070063 result.OpenAnonymousUnit(
64 dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
65 Action::ReadWrite, Position::Rewind, Convert::Native, handler);
peter klauslerbd43fa22020-07-14 11:28:03 -070066 result.isUnformatted = isUnformatted;
67 }
68 return result;
69}
70
peter klausler675ad1b2020-08-03 11:35:29 -070071ExternalFileUnit *ExternalFileUnit::LookUp(const char *path) {
72 return GetUnitMap().LookUp(path);
73}
74
peter klauslerbd43fa22020-07-14 11:28:03 -070075ExternalFileUnit &ExternalFileUnit::CreateNew(
76 int unit, const Terminator &terminator) {
77 bool wasExtant{false};
78 ExternalFileUnit &result{
79 GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant)};
80 RUNTIME_CHECK(terminator, !wasExtant);
81 return result;
82}
83
peter klausler3b635712020-02-13 14:41:56 -080084ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) {
85 return GetUnitMap().LookUpForClose(unit);
86}
87
88int ExternalFileUnit::NewUnit(const Terminator &terminator) {
89 return GetUnitMap().NewUnit(terminator).unitNumber();
peter klausler95696d52020-02-04 16:55:45 -080090}
91
peter klauslerea4758a2020-07-17 11:24:29 -070092void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
93 Position position, OwningPtr<char> &&newPath, std::size_t newPathLength,
peter klausler8f2c5c42020-07-21 17:37:35 -070094 Convert convert, IoErrorHandler &handler) {
95 if (executionEnvironment.conversion != Convert::Unknown) {
96 convert = executionEnvironment.conversion;
97 }
98 swapEndianness_ = convert == Convert::Swap ||
99 (convert == Convert::LittleEndian && !isHostLittleEndian) ||
100 (convert == Convert::BigEndian && isHostLittleEndian);
peter klausler95696d52020-02-04 16:55:45 -0800101 if (IsOpen()) {
102 if (status == OpenStatus::Old &&
103 (!newPath.get() ||
104 (path() && pathLength() == newPathLength &&
105 std::memcmp(path(), newPath.get(), newPathLength) == 0))) {
106 // OPEN of existing unit, STATUS='OLD', not new FILE=
107 newPath.reset();
108 return;
109 }
110 // Otherwise, OPEN on open unit with new FILE= implies CLOSE
peter klausler00063542020-07-03 12:38:22 -0700111 DoImpliedEndfile(handler);
peter klausler95696d52020-02-04 16:55:45 -0800112 Flush(handler);
113 Close(CloseStatus::Keep, handler);
114 }
115 set_path(std::move(newPath), newPathLength);
peter klauslerea4758a2020-07-17 11:24:29 -0700116 Open(status, action, position, handler);
peter klausler00063542020-07-03 12:38:22 -0700117 auto totalBytes{knownSize()};
118 if (access == Access::Direct) {
119 if (!isFixedRecordLength || !recordLength) {
120 handler.SignalError(IostatOpenBadRecl,
121 "OPEN(UNIT=%d,ACCESS='DIRECT'): record length is not known",
122 unitNumber());
123 } else if (*recordLength <= 0) {
124 handler.SignalError(IostatOpenBadRecl,
125 "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
126 unitNumber(), static_cast<std::intmax_t>(*recordLength));
peter klausler675ad1b2020-08-03 11:35:29 -0700127 } else if (totalBytes && (*totalBytes % *recordLength != 0)) {
peter klausler00063542020-07-03 12:38:22 -0700128 handler.SignalError(IostatOpenBadAppend,
129 "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
130 "even divisor of the file size %jd",
131 unitNumber(), static_cast<std::intmax_t>(*recordLength),
132 static_cast<std::intmax_t>(*totalBytes));
133 }
134 }
135 if (position == Position::Append) {
peter klausler675ad1b2020-08-03 11:35:29 -0700136 if (totalBytes && recordLength && *recordLength) {
peter klausler00063542020-07-03 12:38:22 -0700137 endfileRecordNumber = 1 + (*totalBytes / *recordLength);
138 } else {
139 // Fake it so that we can backspace relative from the end
140 endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 1;
141 }
142 currentRecordNumber = *endfileRecordNumber;
143 } else {
144 currentRecordNumber = 1;
145 }
peter klausler95696d52020-02-04 16:55:45 -0800146}
147
peter klausler675ad1b2020-08-03 11:35:29 -0700148void ExternalFileUnit::OpenAnonymousUnit(OpenStatus status,
149 std::optional<Action> action, Position position, Convert convert,
150 IoErrorHandler &handler) {
151 // I/O to an unconnected unit reads/creates a local file, e.g. fort.7
152 std::size_t pathMaxLen{32};
153 auto path{SizedNew<char>{handler}(pathMaxLen)};
154 std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_);
155 OpenUnit(status, action, position, std::move(path), std::strlen(path.get()),
156 convert, handler);
157}
158
peter klausler95696d52020-02-04 16:55:45 -0800159void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700160 DoImpliedEndfile(handler);
peter klausler3b635712020-02-13 14:41:56 -0800161 Flush(handler);
162 Close(status, handler);
peter klauslerf7be2512020-01-23 16:59:27 -0800163}
164
peter klausler3b635712020-02-13 14:41:56 -0800165void ExternalFileUnit::DestroyClosed() {
Tim Keith1f879002020-03-28 21:00:16 -0700166 GetUnitMap().DestroyClosed(*this); // destroys *this
peter klausler3b635712020-02-13 14:41:56 -0800167}
168
peter klausler00063542020-07-03 12:38:22 -0700169bool ExternalFileUnit::SetDirection(
170 Direction direction, IoErrorHandler &handler) {
171 if (direction == Direction::Input) {
172 if (mayRead()) {
173 direction_ = Direction::Input;
174 return true;
175 } else {
176 handler.SignalError(IostatReadFromWriteOnly,
177 "READ(UNIT=%d) with ACTION='WRITE'", unitNumber());
178 return false;
179 }
180 } else {
181 if (mayWrite()) {
182 direction_ = Direction::Output;
183 return true;
184 } else {
185 handler.SignalError(IostatWriteToReadOnly,
186 "WRITE(UNIT=%d) with ACTION='READ'", unitNumber());
187 return false;
188 }
189 }
190}
191
peter klausler3b635712020-02-13 14:41:56 -0800192UnitMap &ExternalFileUnit::GetUnitMap() {
193 if (unitMap) {
194 return *unitMap;
195 }
196 CriticalSection critical{unitMapLock};
197 if (unitMap) {
198 return *unitMap;
199 }
200 Terminator terminator{__FILE__, __LINE__};
peter klausler00063542020-07-03 12:38:22 -0700201 IoErrorHandler handler{terminator};
peter klausler98d576c2020-07-02 18:35:20 -0700202 unitMap = New<UnitMap>{terminator}().release();
peter klauslerbd43fa22020-07-14 11:28:03 -0700203 ExternalFileUnit &out{ExternalFileUnit::CreateNew(6, terminator)};
peter klauslerf7be2512020-01-23 16:59:27 -0800204 out.Predefine(1);
peter klausler00063542020-07-03 12:38:22 -0700205 out.SetDirection(Direction::Output, handler);
peter klausler95696d52020-02-04 16:55:45 -0800206 defaultOutput = &out;
peter klauslerbd43fa22020-07-14 11:28:03 -0700207 ExternalFileUnit &in{ExternalFileUnit::CreateNew(5, terminator)};
peter klauslerf7be2512020-01-23 16:59:27 -0800208 in.Predefine(0);
peter klausler00063542020-07-03 12:38:22 -0700209 in.SetDirection(Direction::Input, handler);
210 defaultInput = &in;
peter klauslerf7be2512020-01-23 16:59:27 -0800211 // TODO: Set UTF-8 mode from the environment
peter klausler3b635712020-02-13 14:41:56 -0800212 return *unitMap;
peter klauslerf7be2512020-01-23 16:59:27 -0800213}
214
peter klausler95696d52020-02-04 16:55:45 -0800215void ExternalFileUnit::CloseAll(IoErrorHandler &handler) {
peter klausler3b635712020-02-13 14:41:56 -0800216 CriticalSection critical{unitMapLock};
217 if (unitMap) {
218 unitMap->CloseAll(handler);
219 FreeMemoryAndNullify(unitMap);
220 }
peter klausler95696d52020-02-04 16:55:45 -0800221 defaultOutput = nullptr;
peter klauslerf7be2512020-01-23 16:59:27 -0800222}
223
peter klausler00063542020-07-03 12:38:22 -0700224void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {
225 CriticalSection critical{unitMapLock};
226 if (unitMap) {
227 unitMap->FlushAll(handler);
228 }
229}
230
peter klausler8f2c5c42020-07-21 17:37:35 -0700231static void SwapEndianness(
232 char *data, std::size_t bytes, std::size_t elementBytes) {
233 if (elementBytes > 1) {
234 auto half{elementBytes >> 1};
235 for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
236 for (std::size_t k{0}; k < half; ++k) {
237 std::swap(data[j + k], data[j + elementBytes - 1 - k]);
238 }
239 }
240 }
241}
242
243bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
244 std::size_t elementBytes, IoErrorHandler &handler) {
peter klausler95696d52020-02-04 16:55:45 -0800245 auto furthestAfter{std::max(furthestPositionInRecord,
246 positionInRecord + static_cast<std::int64_t>(bytes))};
peter klausler3b635712020-02-13 14:41:56 -0800247 if (furthestAfter > recordLength.value_or(furthestAfter)) {
peter klausler00063542020-07-03 12:38:22 -0700248 handler.SignalError(IostatRecordWriteOverrun,
249 "Attempt to write %zd bytes to position %jd in a fixed-size record of "
250 "%jd bytes",
251 bytes, static_cast<std::intmax_t>(positionInRecord),
252 static_cast<std::intmax_t>(*recordLength));
peter klausler3b635712020-02-13 14:41:56 -0800253 return false;
254 }
255 WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
peter klausler27505562020-06-18 12:19:49 -0700256 if (positionInRecord > furthestPositionInRecord) {
peter klausler00063542020-07-03 12:38:22 -0700257 std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
peter klausler27505562020-06-18 12:19:49 -0700258 positionInRecord - furthestPositionInRecord);
259 }
peter klausler8f2c5c42020-07-21 17:37:35 -0700260 char *to{Frame() + recordOffsetInFrame_ + positionInRecord};
261 std::memcpy(to, data, bytes);
262 if (swapEndianness_) {
263 SwapEndianness(to, bytes, elementBytes);
264 }
peter klauslerf7be2512020-01-23 16:59:27 -0800265 positionInRecord += bytes;
266 furthestPositionInRecord = furthestAfter;
267 return true;
268}
269
peter klausler8f2c5c42020-07-21 17:37:35 -0700270bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
271 std::size_t elementBytes, IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700272 RUNTIME_CHECK(handler, direction_ == Direction::Input);
273 auto furthestAfter{std::max(furthestPositionInRecord,
274 positionInRecord + static_cast<std::int64_t>(bytes))};
275 if (furthestAfter > recordLength.value_or(furthestAfter)) {
276 handler.SignalError(IostatRecordReadOverrun,
277 "Attempt to read %zd bytes at position %jd in a record of %jd bytes",
278 bytes, static_cast<std::intmax_t>(positionInRecord),
279 static_cast<std::intmax_t>(*recordLength));
280 return false;
281 }
282 auto need{recordOffsetInFrame_ + furthestAfter};
283 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
284 if (got >= need) {
285 std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes);
peter klausler8f2c5c42020-07-21 17:37:35 -0700286 if (swapEndianness_) {
287 SwapEndianness(data, bytes, elementBytes);
288 }
peter klausler00063542020-07-03 12:38:22 -0700289 positionInRecord += bytes;
290 furthestPositionInRecord = furthestAfter;
291 return true;
292 } else {
293 handler.SignalEnd();
294 endfileRecordNumber = currentRecordNumber;
295 return false;
296 }
297}
298
peter klausler3b635712020-02-13 14:41:56 -0800299std::optional<char32_t> ExternalFileUnit::GetCurrentChar(
300 IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700301 RUNTIME_CHECK(handler, direction_ == Direction::Input);
302 if (const char *p{FrameNextInput(handler, 1)}) {
303 // TODO: UTF-8 decoding; may have to get more bytes in a loop
304 return *p;
peter klausler3b635712020-02-13 14:41:56 -0800305 }
peter klausler00063542020-07-03 12:38:22 -0700306 return std::nullopt;
307}
308
309const char *ExternalFileUnit::FrameNextInput(
310 IoErrorHandler &handler, std::size_t bytes) {
311 RUNTIME_CHECK(handler, !isUnformatted);
312 if (static_cast<std::int64_t>(positionInRecord + bytes) <=
313 recordLength.value_or(positionInRecord + bytes)) {
314 auto at{recordOffsetInFrame_ + positionInRecord};
315 auto need{static_cast<std::size_t>(at + bytes)};
316 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
317 SetSequentialVariableFormattedRecordLength();
318 if (got >= need) {
319 return Frame() + at;
peter klausler3b635712020-02-13 14:41:56 -0800320 }
peter klausler3b635712020-02-13 14:41:56 -0800321 handler.SignalEnd();
peter klausler00063542020-07-03 12:38:22 -0700322 endfileRecordNumber = currentRecordNumber;
peter klausler3b635712020-02-13 14:41:56 -0800323 }
peter klausler00063542020-07-03 12:38:22 -0700324 return nullptr;
325}
326
327bool ExternalFileUnit::SetSequentialVariableFormattedRecordLength() {
328 if (recordLength || access != Access::Sequential) {
329 return true;
peter klausler3b635712020-02-13 14:41:56 -0800330 }
peter klausler00063542020-07-03 12:38:22 -0700331 if (FrameLength() > recordOffsetInFrame_) {
332 const char *record{Frame() + recordOffsetInFrame_};
333 if (const char *nl{reinterpret_cast<const char *>(
334 std::memchr(record, '\n', FrameLength() - recordOffsetInFrame_))}) {
335 recordLength = nl - record;
336 if (*recordLength > 0 && record[*recordLength - 1] == '\r') {
337 --*recordLength;
338 }
339 return true;
340 }
341 }
342 return false;
peter klausler3b635712020-02-13 14:41:56 -0800343}
344
peter klausler95696d52020-02-04 16:55:45 -0800345void ExternalFileUnit::SetLeftTabLimit() {
peter klauslerf7be2512020-01-23 16:59:27 -0800346 leftTabLimit = furthestPositionInRecord;
347 positionInRecord = furthestPositionInRecord;
348}
349
peter klausler00063542020-07-03 12:38:22 -0700350void ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
351 RUNTIME_CHECK(handler, direction_ == Direction::Input);
peter klauslere24f0ac2020-09-30 12:53:00 -0700352 if (beganReadingRecord_) {
353 return;
354 }
355 beganReadingRecord_ = true;
peter klausler00063542020-07-03 12:38:22 -0700356 if (access == Access::Sequential) {
357 if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
358 handler.SignalEnd();
359 } else if (isFixedRecordLength) {
360 RUNTIME_CHECK(handler, recordLength.has_value());
361 auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *recordLength)};
362 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
363 if (got < need) {
364 handler.SignalEnd();
peter klausler3b635712020-02-13 14:41:56 -0800365 }
peter klausler00063542020-07-03 12:38:22 -0700366 } else if (isUnformatted) {
367 BeginSequentialVariableUnformattedInputRecord(handler);
368 } else { // formatted
369 BeginSequentialVariableFormattedInputRecord(handler);
peter klausler3b635712020-02-13 14:41:56 -0800370 }
peter klauslerf7be2512020-01-23 16:59:27 -0800371 }
peter klausler00063542020-07-03 12:38:22 -0700372}
373
peter klauslere24f0ac2020-09-30 12:53:00 -0700374void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
375 RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_);
376 beganReadingRecord_ = false;
377 if (access == Access::Sequential) {
378 RUNTIME_CHECK(handler, recordLength.has_value());
379 if (isFixedRecordLength) {
380 frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
381 recordOffsetInFrame_ = 0;
382 } else if (isUnformatted) {
383 // Retain footer in frame for more efficient BACKSPACE
384 frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
385 recordOffsetInFrame_ = sizeof(std::uint32_t);
386 recordLength.reset();
387 } else { // formatted
388 if (Frame()[recordOffsetInFrame_ + *recordLength] == '\r') {
389 ++recordOffsetInFrame_;
390 }
391 recordOffsetInFrame_ += *recordLength + 1;
392 RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ - 1] == '\n');
393 recordLength.reset();
394 }
395 }
396 ++currentRecordNumber;
397 BeginRecord();
398}
399
peter klausler00063542020-07-03 12:38:22 -0700400bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
401 bool ok{true};
402 if (direction_ == Direction::Input) {
peter klauslere24f0ac2020-09-30 12:53:00 -0700403 FinishReadingRecord(handler);
404 BeginReadingRecord(handler);
peter klausler00063542020-07-03 12:38:22 -0700405 } else { // Direction::Output
406 if (!isUnformatted) {
407 if (isFixedRecordLength && recordLength) {
408 if (furthestPositionInRecord < *recordLength) {
409 WriteFrame(frameOffsetInFile_, *recordLength, handler);
410 std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
411 ' ', *recordLength - furthestPositionInRecord);
412 }
413 } else {
414 positionInRecord = furthestPositionInRecord;
peter klausler8f2c5c42020-07-21 17:37:35 -0700415 ok &= Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
peter klausler00063542020-07-03 12:38:22 -0700416 }
417 }
418 frameOffsetInFile_ +=
419 recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord);
420 recordOffsetInFrame_ = 0;
421 impliedEndfile_ = true;
peter klauslere24f0ac2020-09-30 12:53:00 -0700422 ++currentRecordNumber;
423 BeginRecord();
peter klausler00063542020-07-03 12:38:22 -0700424 }
peter klauslerf7be2512020-01-23 16:59:27 -0800425 return ok;
426}
427
peter klausler3b635712020-02-13 14:41:56 -0800428void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700429 if (access != Access::Sequential) {
430 handler.SignalError(IostatBackspaceNonSequential,
431 "BACKSPACE(UNIT=%d) on non-sequential file", unitNumber());
peter klausler3b635712020-02-13 14:41:56 -0800432 } else {
peter klausler00063542020-07-03 12:38:22 -0700433 DoImpliedEndfile(handler);
434 --currentRecordNumber;
435 BeginRecord();
436 if (isFixedRecordLength) {
437 BackspaceFixedRecord(handler);
438 } else if (isUnformatted) {
439 BackspaceVariableUnformattedRecord(handler);
440 } else {
441 BackspaceVariableFormattedRecord(handler);
442 }
peter klausler3b635712020-02-13 14:41:56 -0800443 }
peter klauslerf7be2512020-01-23 16:59:27 -0800444}
445
peter klausler95696d52020-02-04 16:55:45 -0800446void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) {
447 if (isTerminal()) {
448 Flush(handler);
449 }
450}
451
peter klausler00063542020-07-03 12:38:22 -0700452void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
453 if (access != Access::Sequential) {
454 handler.SignalError(IostatEndfileNonSequential,
455 "ENDFILE(UNIT=%d) on non-sequential file", unitNumber());
456 } else if (!mayWrite()) {
457 handler.SignalError(IostatEndfileUnwritable,
458 "ENDFILE(UNIT=%d) on read-only file", unitNumber());
459 } else {
460 DoEndfile(handler);
461 }
462}
463
464void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
465 if (access == Access::Direct) {
466 handler.SignalError(IostatRewindNonSequential,
467 "REWIND(UNIT=%d) on non-sequential file", unitNumber());
468 } else {
469 DoImpliedEndfile(handler);
470 SetPosition(0);
471 currentRecordNumber = 1;
472 // TODO: reset endfileRecordNumber?
473 }
474}
475
peter klausler95696d52020-02-04 16:55:45 -0800476void ExternalFileUnit::EndIoStatement() {
peter klausler3b635712020-02-13 14:41:56 -0800477 frameOffsetInFile_ += recordOffsetInFrame_;
478 recordOffsetInFrame_ = 0;
peter klausler95696d52020-02-04 16:55:45 -0800479 io_.reset();
peter klauslerf7be2512020-01-23 16:59:27 -0800480 u_.emplace<std::monostate>();
peter klausler3b635712020-02-13 14:41:56 -0800481 lock_.Drop();
482}
483
peter klausler00063542020-07-03 12:38:22 -0700484void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
peter klausler3b635712020-02-13 14:41:56 -0800485 IoErrorHandler &handler) {
486 std::int32_t header{0}, footer{0};
peter klausler00063542020-07-03 12:38:22 -0700487 std::size_t need{recordOffsetInFrame_ + sizeof header};
488 std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)};
peter klausler3b635712020-02-13 14:41:56 -0800489 // Try to emit informative errors to help debug corrupted files.
490 const char *error{nullptr};
491 if (got < need) {
peter klausler00063542020-07-03 12:38:22 -0700492 if (got == recordOffsetInFrame_) {
peter klausler3b635712020-02-13 14:41:56 -0800493 handler.SignalEnd();
494 } else {
peter klausler00063542020-07-03 12:38:22 -0700495 error = "Unformatted variable-length sequential file input failed at "
496 "record #%jd (file offset %jd): truncated record header";
peter klausler3b635712020-02-13 14:41:56 -0800497 }
498 } else {
peter klausler00063542020-07-03 12:38:22 -0700499 std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
500 recordLength = sizeof header + header; // does not include footer
501 need = recordOffsetInFrame_ + *recordLength + sizeof footer;
502 got = ReadFrame(frameOffsetInFile_, need, handler);
peter klausler3b635712020-02-13 14:41:56 -0800503 if (got < need) {
peter klausler00063542020-07-03 12:38:22 -0700504 error = "Unformatted variable-length sequential file input failed at "
505 "record #%jd (file offset %jd): hit EOF reading record with "
506 "length %jd bytes";
peter klausler3b635712020-02-13 14:41:56 -0800507 } else {
peter klausler00063542020-07-03 12:38:22 -0700508 std::memcpy(&footer, Frame() + recordOffsetInFrame_ + *recordLength,
509 sizeof footer);
peter klausler3b635712020-02-13 14:41:56 -0800510 if (footer != header) {
peter klausler00063542020-07-03 12:38:22 -0700511 error = "Unformatted variable-length sequential file input failed at "
512 "record #%jd (file offset %jd): record header has length %jd "
513 "that does not match record footer (%jd)";
peter klausler3b635712020-02-13 14:41:56 -0800514 }
515 }
516 }
517 if (error) {
518 handler.SignalError(error, static_cast<std::intmax_t>(currentRecordNumber),
519 static_cast<std::intmax_t>(frameOffsetInFile_),
520 static_cast<std::intmax_t>(header), static_cast<std::intmax_t>(footer));
peter klausler00063542020-07-03 12:38:22 -0700521 // TODO: error recovery
peter klausler3b635712020-02-13 14:41:56 -0800522 }
523 positionInRecord = sizeof header;
524}
525
peter klausler00063542020-07-03 12:38:22 -0700526void ExternalFileUnit::BeginSequentialVariableFormattedInputRecord(
peter klausler3b635712020-02-13 14:41:56 -0800527 IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700528 if (this == defaultInput && defaultOutput) {
529 defaultOutput->Flush(handler);
peter klausler3b635712020-02-13 14:41:56 -0800530 }
peter klausler00063542020-07-03 12:38:22 -0700531 std::size_t length{0};
532 do {
533 std::size_t need{recordOffsetInFrame_ + length + 1};
534 length = ReadFrame(frameOffsetInFile_, need, handler);
535 if (length < need) {
peter klausler3b635712020-02-13 14:41:56 -0800536 handler.SignalEnd();
537 break;
538 }
peter klausler00063542020-07-03 12:38:22 -0700539 } while (!SetSequentialVariableFormattedRecordLength());
540}
541
542void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) {
543 RUNTIME_CHECK(handler, recordLength.has_value());
544 if (frameOffsetInFile_ < *recordLength) {
545 handler.SignalError(IostatBackspaceAtFirstRecord);
546 } else {
547 frameOffsetInFile_ -= *recordLength;
peter klausler3b635712020-02-13 14:41:56 -0800548 }
549}
550
peter klausler00063542020-07-03 12:38:22 -0700551void ExternalFileUnit::BackspaceVariableUnformattedRecord(
peter klausler3b635712020-02-13 14:41:56 -0800552 IoErrorHandler &handler) {
553 std::int32_t header{0}, footer{0};
peter klausler00063542020-07-03 12:38:22 -0700554 auto headerBytes{static_cast<std::int64_t>(sizeof header)};
555 frameOffsetInFile_ += recordOffsetInFrame_;
556 recordOffsetInFrame_ = 0;
557 if (frameOffsetInFile_ <= headerBytes) {
558 handler.SignalError(IostatBackspaceAtFirstRecord);
559 return;
560 }
peter klausler3b635712020-02-13 14:41:56 -0800561 // Error conditions here cause crashes, not file format errors, because the
562 // validity of the file structure before the current record will have been
peter klausler00063542020-07-03 12:38:22 -0700563 // checked informatively in NextSequentialVariableUnformattedInputRecord().
peter klausler3b635712020-02-13 14:41:56 -0800564 std::size_t got{
peter klausler00063542020-07-03 12:38:22 -0700565 ReadFrame(frameOffsetInFile_ - headerBytes, headerBytes, handler)};
peter klausler3b635712020-02-13 14:41:56 -0800566 RUNTIME_CHECK(handler, got >= sizeof footer);
567 std::memcpy(&footer, Frame(), sizeof footer);
peter klausler3b635712020-02-13 14:41:56 -0800568 recordLength = footer;
peter klausler00063542020-07-03 12:38:22 -0700569 RUNTIME_CHECK(handler, frameOffsetInFile_ >= *recordLength + 2 * headerBytes);
570 frameOffsetInFile_ -= *recordLength + 2 * headerBytes;
571 if (frameOffsetInFile_ >= headerBytes) {
572 frameOffsetInFile_ -= headerBytes;
573 recordOffsetInFrame_ = headerBytes;
574 }
575 auto need{static_cast<std::size_t>(
576 recordOffsetInFrame_ + sizeof header + *recordLength)};
577 got = ReadFrame(frameOffsetInFile_, need, handler);
578 RUNTIME_CHECK(handler, got >= need);
579 std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
580 RUNTIME_CHECK(handler, header == *recordLength);
peter klausler3b635712020-02-13 14:41:56 -0800581}
582
583// There's no portable memrchr(), unfortunately, and strrchr() would
584// fail on a record with a NUL, so we have to do it the hard way.
585static const char *FindLastNewline(const char *str, std::size_t length) {
586 for (const char *p{str + length}; p-- > str;) {
587 if (*p == '\n') {
588 return p;
589 }
590 }
591 return nullptr;
592}
593
peter klausler00063542020-07-03 12:38:22 -0700594void ExternalFileUnit::BackspaceVariableFormattedRecord(
peter klausler3b635712020-02-13 14:41:56 -0800595 IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700596 // File offset of previous record's newline
597 auto prevNL{
598 frameOffsetInFile_ + static_cast<std::int64_t>(recordOffsetInFrame_) - 1};
599 if (prevNL < 0) {
600 handler.SignalError(IostatBackspaceAtFirstRecord);
601 return;
602 }
603 while (true) {
604 if (frameOffsetInFile_ < prevNL) {
peter klausler3b635712020-02-13 14:41:56 -0800605 if (const char *p{
peter klausler00063542020-07-03 12:38:22 -0700606 FindLastNewline(Frame(), prevNL - 1 - frameOffsetInFile_)}) {
peter klausler3b635712020-02-13 14:41:56 -0800607 recordOffsetInFrame_ = p - Frame() + 1;
peter klausler00063542020-07-03 12:38:22 -0700608 *recordLength = prevNL - (frameOffsetInFile_ + recordOffsetInFrame_);
peter klausler3b635712020-02-13 14:41:56 -0800609 break;
610 }
peter klausler3b635712020-02-13 14:41:56 -0800611 }
peter klausler00063542020-07-03 12:38:22 -0700612 if (frameOffsetInFile_ == 0) {
613 recordOffsetInFrame_ = 0;
614 *recordLength = prevNL;
615 break;
616 }
617 frameOffsetInFile_ -= std::min<std::int64_t>(frameOffsetInFile_, 1024);
618 auto need{static_cast<std::size_t>(prevNL + 1 - frameOffsetInFile_)};
619 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
620 RUNTIME_CHECK(handler, got >= need);
peter klausler3b635712020-02-13 14:41:56 -0800621 }
peter klausler3b635712020-02-13 14:41:56 -0800622 RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ + *recordLength] == '\n');
623 if (*recordLength > 0 &&
624 Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') {
625 --*recordLength;
626 }
peter klauslerf7be2512020-01-23 16:59:27 -0800627}
peter klausler00063542020-07-03 12:38:22 -0700628
629void ExternalFileUnit::DoImpliedEndfile(IoErrorHandler &handler) {
630 if (impliedEndfile_) {
631 impliedEndfile_ = false;
632 if (access == Access::Sequential && mayPosition()) {
633 DoEndfile(handler);
634 }
635 }
636}
637
638void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) {
639 endfileRecordNumber = currentRecordNumber;
640 Truncate(frameOffsetInFile_ + recordOffsetInFrame_, handler);
641 BeginRecord();
642 impliedEndfile_ = false;
643}
Tim Keith1f879002020-03-28 21:00:16 -0700644} // namespace Fortran::runtime::io