blob: 77b7a74551d8f3bc53d5117db1a20071bc8a3a6c [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 }
peter klauslere29c9d72020-10-01 09:50:48 -0700135 endfileRecordNumber.reset();
136 currentRecordNumber = 1;
137 if (totalBytes && recordLength && *recordLength) {
138 endfileRecordNumber = 1 + (*totalBytes / *recordLength);
139 }
peter klausler00063542020-07-03 12:38:22 -0700140 if (position == Position::Append) {
peter klauslere29c9d72020-10-01 09:50:48 -0700141 if (!endfileRecordNumber) {
peter klausler00063542020-07-03 12:38:22 -0700142 // Fake it so that we can backspace relative from the end
peter klauslere29c9d72020-10-01 09:50:48 -0700143 endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 2;
peter klausler00063542020-07-03 12:38:22 -0700144 }
145 currentRecordNumber = *endfileRecordNumber;
peter klausler00063542020-07-03 12:38:22 -0700146 }
peter klausler95696d52020-02-04 16:55:45 -0800147}
148
peter klausler675ad1b2020-08-03 11:35:29 -0700149void ExternalFileUnit::OpenAnonymousUnit(OpenStatus status,
150 std::optional<Action> action, Position position, Convert convert,
151 IoErrorHandler &handler) {
152 // I/O to an unconnected unit reads/creates a local file, e.g. fort.7
153 std::size_t pathMaxLen{32};
154 auto path{SizedNew<char>{handler}(pathMaxLen)};
155 std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_);
156 OpenUnit(status, action, position, std::move(path), std::strlen(path.get()),
157 convert, handler);
158}
159
peter klausler95696d52020-02-04 16:55:45 -0800160void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700161 DoImpliedEndfile(handler);
peter klausler3b635712020-02-13 14:41:56 -0800162 Flush(handler);
163 Close(status, handler);
peter klauslerf7be2512020-01-23 16:59:27 -0800164}
165
peter klausler3b635712020-02-13 14:41:56 -0800166void ExternalFileUnit::DestroyClosed() {
Tim Keith1f879002020-03-28 21:00:16 -0700167 GetUnitMap().DestroyClosed(*this); // destroys *this
peter klausler3b635712020-02-13 14:41:56 -0800168}
169
peter klausler00063542020-07-03 12:38:22 -0700170bool ExternalFileUnit::SetDirection(
171 Direction direction, IoErrorHandler &handler) {
172 if (direction == Direction::Input) {
173 if (mayRead()) {
174 direction_ = Direction::Input;
175 return true;
176 } else {
177 handler.SignalError(IostatReadFromWriteOnly,
178 "READ(UNIT=%d) with ACTION='WRITE'", unitNumber());
179 return false;
180 }
181 } else {
182 if (mayWrite()) {
183 direction_ = Direction::Output;
184 return true;
185 } else {
186 handler.SignalError(IostatWriteToReadOnly,
187 "WRITE(UNIT=%d) with ACTION='READ'", unitNumber());
188 return false;
189 }
190 }
191}
192
peter klausler3b635712020-02-13 14:41:56 -0800193UnitMap &ExternalFileUnit::GetUnitMap() {
194 if (unitMap) {
195 return *unitMap;
196 }
197 CriticalSection critical{unitMapLock};
198 if (unitMap) {
199 return *unitMap;
200 }
201 Terminator terminator{__FILE__, __LINE__};
peter klausler00063542020-07-03 12:38:22 -0700202 IoErrorHandler handler{terminator};
peter klausler98d576c2020-07-02 18:35:20 -0700203 unitMap = New<UnitMap>{terminator}().release();
peter klauslerbd43fa22020-07-14 11:28:03 -0700204 ExternalFileUnit &out{ExternalFileUnit::CreateNew(6, terminator)};
peter klauslerf7be2512020-01-23 16:59:27 -0800205 out.Predefine(1);
peter klausler00063542020-07-03 12:38:22 -0700206 out.SetDirection(Direction::Output, handler);
peter klausler95696d52020-02-04 16:55:45 -0800207 defaultOutput = &out;
peter klauslerbd43fa22020-07-14 11:28:03 -0700208 ExternalFileUnit &in{ExternalFileUnit::CreateNew(5, terminator)};
peter klauslerf7be2512020-01-23 16:59:27 -0800209 in.Predefine(0);
peter klausler00063542020-07-03 12:38:22 -0700210 in.SetDirection(Direction::Input, handler);
211 defaultInput = &in;
peter klauslerf7be2512020-01-23 16:59:27 -0800212 // TODO: Set UTF-8 mode from the environment
peter klausler3b635712020-02-13 14:41:56 -0800213 return *unitMap;
peter klauslerf7be2512020-01-23 16:59:27 -0800214}
215
peter klausler95696d52020-02-04 16:55:45 -0800216void ExternalFileUnit::CloseAll(IoErrorHandler &handler) {
peter klausler3b635712020-02-13 14:41:56 -0800217 CriticalSection critical{unitMapLock};
218 if (unitMap) {
219 unitMap->CloseAll(handler);
220 FreeMemoryAndNullify(unitMap);
221 }
peter klausler95696d52020-02-04 16:55:45 -0800222 defaultOutput = nullptr;
peter klauslerf7be2512020-01-23 16:59:27 -0800223}
224
peter klausler00063542020-07-03 12:38:22 -0700225void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {
226 CriticalSection critical{unitMapLock};
227 if (unitMap) {
228 unitMap->FlushAll(handler);
229 }
230}
231
peter klausler8f2c5c42020-07-21 17:37:35 -0700232static void SwapEndianness(
233 char *data, std::size_t bytes, std::size_t elementBytes) {
234 if (elementBytes > 1) {
235 auto half{elementBytes >> 1};
236 for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
237 for (std::size_t k{0}; k < half; ++k) {
238 std::swap(data[j + k], data[j + elementBytes - 1 - k]);
239 }
240 }
241 }
242}
243
244bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
245 std::size_t elementBytes, IoErrorHandler &handler) {
peter klausler95696d52020-02-04 16:55:45 -0800246 auto furthestAfter{std::max(furthestPositionInRecord,
247 positionInRecord + static_cast<std::int64_t>(bytes))};
peter klausler3b635712020-02-13 14:41:56 -0800248 if (furthestAfter > recordLength.value_or(furthestAfter)) {
peter klausler00063542020-07-03 12:38:22 -0700249 handler.SignalError(IostatRecordWriteOverrun,
250 "Attempt to write %zd bytes to position %jd in a fixed-size record of "
251 "%jd bytes",
252 bytes, static_cast<std::intmax_t>(positionInRecord),
253 static_cast<std::intmax_t>(*recordLength));
peter klausler3b635712020-02-13 14:41:56 -0800254 return false;
255 }
256 WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
peter klausler27505562020-06-18 12:19:49 -0700257 if (positionInRecord > furthestPositionInRecord) {
peter klausler00063542020-07-03 12:38:22 -0700258 std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
peter klausler27505562020-06-18 12:19:49 -0700259 positionInRecord - furthestPositionInRecord);
260 }
peter klausler8f2c5c42020-07-21 17:37:35 -0700261 char *to{Frame() + recordOffsetInFrame_ + positionInRecord};
262 std::memcpy(to, data, bytes);
263 if (swapEndianness_) {
264 SwapEndianness(to, bytes, elementBytes);
265 }
peter klauslerf7be2512020-01-23 16:59:27 -0800266 positionInRecord += bytes;
267 furthestPositionInRecord = furthestAfter;
268 return true;
269}
270
peter klausler8f2c5c42020-07-21 17:37:35 -0700271bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
272 std::size_t elementBytes, IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700273 RUNTIME_CHECK(handler, direction_ == Direction::Input);
274 auto furthestAfter{std::max(furthestPositionInRecord,
275 positionInRecord + static_cast<std::int64_t>(bytes))};
276 if (furthestAfter > recordLength.value_or(furthestAfter)) {
277 handler.SignalError(IostatRecordReadOverrun,
278 "Attempt to read %zd bytes at position %jd in a record of %jd bytes",
279 bytes, static_cast<std::intmax_t>(positionInRecord),
280 static_cast<std::intmax_t>(*recordLength));
281 return false;
282 }
283 auto need{recordOffsetInFrame_ + furthestAfter};
284 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
285 if (got >= need) {
286 std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes);
peter klausler8f2c5c42020-07-21 17:37:35 -0700287 if (swapEndianness_) {
288 SwapEndianness(data, bytes, elementBytes);
289 }
peter klausler00063542020-07-03 12:38:22 -0700290 positionInRecord += bytes;
291 furthestPositionInRecord = furthestAfter;
292 return true;
293 } else {
peter klauslercdfb95a2020-10-01 09:32:48 -0700294 // EOF or error: can be handled & has been signaled
peter klausler00063542020-07-03 12:38:22 -0700295 endfileRecordNumber = currentRecordNumber;
296 return false;
297 }
298}
299
peter klausler3b635712020-02-13 14:41:56 -0800300std::optional<char32_t> ExternalFileUnit::GetCurrentChar(
301 IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700302 RUNTIME_CHECK(handler, direction_ == Direction::Input);
303 if (const char *p{FrameNextInput(handler, 1)}) {
304 // TODO: UTF-8 decoding; may have to get more bytes in a loop
305 return *p;
peter klausler3b635712020-02-13 14:41:56 -0800306 }
peter klausler00063542020-07-03 12:38:22 -0700307 return std::nullopt;
308}
309
310const char *ExternalFileUnit::FrameNextInput(
311 IoErrorHandler &handler, std::size_t bytes) {
312 RUNTIME_CHECK(handler, !isUnformatted);
313 if (static_cast<std::int64_t>(positionInRecord + bytes) <=
314 recordLength.value_or(positionInRecord + bytes)) {
315 auto at{recordOffsetInFrame_ + positionInRecord};
316 auto need{static_cast<std::size_t>(at + bytes)};
317 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
318 SetSequentialVariableFormattedRecordLength();
319 if (got >= need) {
320 return Frame() + at;
peter klausler3b635712020-02-13 14:41:56 -0800321 }
peter klausler3b635712020-02-13 14:41:56 -0800322 handler.SignalEnd();
peter klausler00063542020-07-03 12:38:22 -0700323 endfileRecordNumber = currentRecordNumber;
peter klausler3b635712020-02-13 14:41:56 -0800324 }
peter klausler00063542020-07-03 12:38:22 -0700325 return nullptr;
326}
327
328bool ExternalFileUnit::SetSequentialVariableFormattedRecordLength() {
329 if (recordLength || access != Access::Sequential) {
330 return true;
peter klausler3b635712020-02-13 14:41:56 -0800331 }
peter klausler00063542020-07-03 12:38:22 -0700332 if (FrameLength() > recordOffsetInFrame_) {
333 const char *record{Frame() + recordOffsetInFrame_};
334 if (const char *nl{reinterpret_cast<const char *>(
335 std::memchr(record, '\n', FrameLength() - recordOffsetInFrame_))}) {
336 recordLength = nl - record;
337 if (*recordLength > 0 && record[*recordLength - 1] == '\r') {
338 --*recordLength;
339 }
340 return true;
341 }
342 }
343 return false;
peter klausler3b635712020-02-13 14:41:56 -0800344}
345
peter klausler95696d52020-02-04 16:55:45 -0800346void ExternalFileUnit::SetLeftTabLimit() {
peter klauslerf7be2512020-01-23 16:59:27 -0800347 leftTabLimit = furthestPositionInRecord;
348 positionInRecord = furthestPositionInRecord;
349}
350
peter klausler00063542020-07-03 12:38:22 -0700351void ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
352 RUNTIME_CHECK(handler, direction_ == Direction::Input);
peter klauslere24f0ac2020-09-30 12:53:00 -0700353 if (beganReadingRecord_) {
354 return;
355 }
356 beganReadingRecord_ = true;
peter klausler00063542020-07-03 12:38:22 -0700357 if (access == Access::Sequential) {
358 if (endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber) {
359 handler.SignalEnd();
360 } else if (isFixedRecordLength) {
361 RUNTIME_CHECK(handler, recordLength.has_value());
362 auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *recordLength)};
363 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
364 if (got < need) {
365 handler.SignalEnd();
peter klausler3b635712020-02-13 14:41:56 -0800366 }
peter klausler00063542020-07-03 12:38:22 -0700367 } else if (isUnformatted) {
368 BeginSequentialVariableUnformattedInputRecord(handler);
369 } else { // formatted
370 BeginSequentialVariableFormattedInputRecord(handler);
peter klausler3b635712020-02-13 14:41:56 -0800371 }
peter klauslerf7be2512020-01-23 16:59:27 -0800372 }
peter klausler00063542020-07-03 12:38:22 -0700373}
374
peter klauslere24f0ac2020-09-30 12:53:00 -0700375void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
376 RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_);
377 beganReadingRecord_ = false;
peter klauslere29c9d72020-10-01 09:50:48 -0700378 if (handler.GetIoStat() != IostatOk) {
379 // avoid bogus crashes in END/ERR circumstances
380 } else if (access == Access::Sequential) {
peter klauslere24f0ac2020-09-30 12:53:00 -0700381 RUNTIME_CHECK(handler, recordLength.has_value());
382 if (isFixedRecordLength) {
383 frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
384 recordOffsetInFrame_ = 0;
385 } else if (isUnformatted) {
386 // Retain footer in frame for more efficient BACKSPACE
387 frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength;
388 recordOffsetInFrame_ = sizeof(std::uint32_t);
389 recordLength.reset();
390 } else { // formatted
391 if (Frame()[recordOffsetInFrame_ + *recordLength] == '\r') {
392 ++recordOffsetInFrame_;
393 }
394 recordOffsetInFrame_ += *recordLength + 1;
395 RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ - 1] == '\n');
396 recordLength.reset();
397 }
398 }
399 ++currentRecordNumber;
400 BeginRecord();
401}
402
peter klausler00063542020-07-03 12:38:22 -0700403bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
404 bool ok{true};
405 if (direction_ == Direction::Input) {
peter klauslere24f0ac2020-09-30 12:53:00 -0700406 FinishReadingRecord(handler);
407 BeginReadingRecord(handler);
peter klausler00063542020-07-03 12:38:22 -0700408 } else { // Direction::Output
409 if (!isUnformatted) {
410 if (isFixedRecordLength && recordLength) {
411 if (furthestPositionInRecord < *recordLength) {
412 WriteFrame(frameOffsetInFile_, *recordLength, handler);
413 std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
414 ' ', *recordLength - furthestPositionInRecord);
415 }
416 } else {
417 positionInRecord = furthestPositionInRecord;
peter klausler8f2c5c42020-07-21 17:37:35 -0700418 ok &= Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
peter klausler00063542020-07-03 12:38:22 -0700419 }
420 }
421 frameOffsetInFile_ +=
422 recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord);
423 recordOffsetInFrame_ = 0;
424 impliedEndfile_ = true;
peter klauslere24f0ac2020-09-30 12:53:00 -0700425 ++currentRecordNumber;
426 BeginRecord();
peter klausler00063542020-07-03 12:38:22 -0700427 }
peter klauslerf7be2512020-01-23 16:59:27 -0800428 return ok;
429}
430
peter klausler3b635712020-02-13 14:41:56 -0800431void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700432 if (access != Access::Sequential) {
433 handler.SignalError(IostatBackspaceNonSequential,
434 "BACKSPACE(UNIT=%d) on non-sequential file", unitNumber());
peter klausler3b635712020-02-13 14:41:56 -0800435 } else {
peter klauslere29c9d72020-10-01 09:50:48 -0700436 if (endfileRecordNumber && currentRecordNumber > *endfileRecordNumber) {
437 // BACKSPACE after ENDFILE
peter klausler00063542020-07-03 12:38:22 -0700438 } else {
peter klauslere29c9d72020-10-01 09:50:48 -0700439 DoImpliedEndfile(handler);
440 if (frameOffsetInFile_ + recordOffsetInFrame_ > 0) {
441 --currentRecordNumber;
442 if (isFixedRecordLength) {
443 BackspaceFixedRecord(handler);
444 } else if (isUnformatted) {
445 BackspaceVariableUnformattedRecord(handler);
446 } else {
447 BackspaceVariableFormattedRecord(handler);
448 }
449 }
peter klausler00063542020-07-03 12:38:22 -0700450 }
peter klauslere29c9d72020-10-01 09:50:48 -0700451 BeginRecord();
peter klausler3b635712020-02-13 14:41:56 -0800452 }
peter klauslerf7be2512020-01-23 16:59:27 -0800453}
454
peter klausler95696d52020-02-04 16:55:45 -0800455void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) {
456 if (isTerminal()) {
457 Flush(handler);
458 }
459}
460
peter klausler00063542020-07-03 12:38:22 -0700461void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
462 if (access != Access::Sequential) {
463 handler.SignalError(IostatEndfileNonSequential,
464 "ENDFILE(UNIT=%d) on non-sequential file", unitNumber());
465 } else if (!mayWrite()) {
466 handler.SignalError(IostatEndfileUnwritable,
467 "ENDFILE(UNIT=%d) on read-only file", unitNumber());
peter klauslere29c9d72020-10-01 09:50:48 -0700468 } else if (endfileRecordNumber &&
469 currentRecordNumber > *endfileRecordNumber) {
470 // ENDFILE after ENDFILE
peter klausler00063542020-07-03 12:38:22 -0700471 } else {
472 DoEndfile(handler);
peter klauslere29c9d72020-10-01 09:50:48 -0700473 ++currentRecordNumber;
peter klausler00063542020-07-03 12:38:22 -0700474 }
475}
476
477void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
478 if (access == Access::Direct) {
479 handler.SignalError(IostatRewindNonSequential,
480 "REWIND(UNIT=%d) on non-sequential file", unitNumber());
481 } else {
482 DoImpliedEndfile(handler);
483 SetPosition(0);
484 currentRecordNumber = 1;
peter klausler00063542020-07-03 12:38:22 -0700485 }
486}
487
peter klausler95696d52020-02-04 16:55:45 -0800488void ExternalFileUnit::EndIoStatement() {
peter klausler3b635712020-02-13 14:41:56 -0800489 frameOffsetInFile_ += recordOffsetInFrame_;
490 recordOffsetInFrame_ = 0;
peter klausler95696d52020-02-04 16:55:45 -0800491 io_.reset();
peter klauslerf7be2512020-01-23 16:59:27 -0800492 u_.emplace<std::monostate>();
peter klausler3b635712020-02-13 14:41:56 -0800493 lock_.Drop();
494}
495
peter klausler00063542020-07-03 12:38:22 -0700496void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
peter klausler3b635712020-02-13 14:41:56 -0800497 IoErrorHandler &handler) {
498 std::int32_t header{0}, footer{0};
peter klausler00063542020-07-03 12:38:22 -0700499 std::size_t need{recordOffsetInFrame_ + sizeof header};
500 std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)};
peter klausler3b635712020-02-13 14:41:56 -0800501 // Try to emit informative errors to help debug corrupted files.
502 const char *error{nullptr};
503 if (got < need) {
peter klausler00063542020-07-03 12:38:22 -0700504 if (got == recordOffsetInFrame_) {
peter klausler3b635712020-02-13 14:41:56 -0800505 handler.SignalEnd();
506 } else {
peter klausler00063542020-07-03 12:38:22 -0700507 error = "Unformatted variable-length sequential file input failed at "
508 "record #%jd (file offset %jd): truncated record header";
peter klausler3b635712020-02-13 14:41:56 -0800509 }
510 } else {
peter klausler00063542020-07-03 12:38:22 -0700511 std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
512 recordLength = sizeof header + header; // does not include footer
513 need = recordOffsetInFrame_ + *recordLength + sizeof footer;
514 got = ReadFrame(frameOffsetInFile_, need, handler);
peter klausler3b635712020-02-13 14:41:56 -0800515 if (got < need) {
peter klausler00063542020-07-03 12:38:22 -0700516 error = "Unformatted variable-length sequential file input failed at "
517 "record #%jd (file offset %jd): hit EOF reading record with "
518 "length %jd bytes";
peter klausler3b635712020-02-13 14:41:56 -0800519 } else {
peter klausler00063542020-07-03 12:38:22 -0700520 std::memcpy(&footer, Frame() + recordOffsetInFrame_ + *recordLength,
521 sizeof footer);
peter klausler3b635712020-02-13 14:41:56 -0800522 if (footer != header) {
peter klausler00063542020-07-03 12:38:22 -0700523 error = "Unformatted variable-length sequential file input failed at "
524 "record #%jd (file offset %jd): record header has length %jd "
525 "that does not match record footer (%jd)";
peter klausler3b635712020-02-13 14:41:56 -0800526 }
527 }
528 }
529 if (error) {
530 handler.SignalError(error, static_cast<std::intmax_t>(currentRecordNumber),
531 static_cast<std::intmax_t>(frameOffsetInFile_),
532 static_cast<std::intmax_t>(header), static_cast<std::intmax_t>(footer));
peter klausler00063542020-07-03 12:38:22 -0700533 // TODO: error recovery
peter klausler3b635712020-02-13 14:41:56 -0800534 }
535 positionInRecord = sizeof header;
536}
537
peter klausler00063542020-07-03 12:38:22 -0700538void ExternalFileUnit::BeginSequentialVariableFormattedInputRecord(
peter klausler3b635712020-02-13 14:41:56 -0800539 IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700540 if (this == defaultInput && defaultOutput) {
541 defaultOutput->Flush(handler);
peter klausler3b635712020-02-13 14:41:56 -0800542 }
peter klausler00063542020-07-03 12:38:22 -0700543 std::size_t length{0};
544 do {
545 std::size_t need{recordOffsetInFrame_ + length + 1};
546 length = ReadFrame(frameOffsetInFile_, need, handler);
547 if (length < need) {
peter klausler3b635712020-02-13 14:41:56 -0800548 handler.SignalEnd();
549 break;
550 }
peter klausler00063542020-07-03 12:38:22 -0700551 } while (!SetSequentialVariableFormattedRecordLength());
552}
553
554void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) {
555 RUNTIME_CHECK(handler, recordLength.has_value());
556 if (frameOffsetInFile_ < *recordLength) {
557 handler.SignalError(IostatBackspaceAtFirstRecord);
558 } else {
559 frameOffsetInFile_ -= *recordLength;
peter klausler3b635712020-02-13 14:41:56 -0800560 }
561}
562
peter klausler00063542020-07-03 12:38:22 -0700563void ExternalFileUnit::BackspaceVariableUnformattedRecord(
peter klausler3b635712020-02-13 14:41:56 -0800564 IoErrorHandler &handler) {
565 std::int32_t header{0}, footer{0};
peter klausler00063542020-07-03 12:38:22 -0700566 auto headerBytes{static_cast<std::int64_t>(sizeof header)};
567 frameOffsetInFile_ += recordOffsetInFrame_;
568 recordOffsetInFrame_ = 0;
569 if (frameOffsetInFile_ <= headerBytes) {
570 handler.SignalError(IostatBackspaceAtFirstRecord);
571 return;
572 }
peter klausler3b635712020-02-13 14:41:56 -0800573 // Error conditions here cause crashes, not file format errors, because the
574 // validity of the file structure before the current record will have been
peter klausler00063542020-07-03 12:38:22 -0700575 // checked informatively in NextSequentialVariableUnformattedInputRecord().
peter klausler3b635712020-02-13 14:41:56 -0800576 std::size_t got{
peter klausler00063542020-07-03 12:38:22 -0700577 ReadFrame(frameOffsetInFile_ - headerBytes, headerBytes, handler)};
peter klausler3b635712020-02-13 14:41:56 -0800578 RUNTIME_CHECK(handler, got >= sizeof footer);
579 std::memcpy(&footer, Frame(), sizeof footer);
peter klausler3b635712020-02-13 14:41:56 -0800580 recordLength = footer;
peter klausler00063542020-07-03 12:38:22 -0700581 RUNTIME_CHECK(handler, frameOffsetInFile_ >= *recordLength + 2 * headerBytes);
582 frameOffsetInFile_ -= *recordLength + 2 * headerBytes;
583 if (frameOffsetInFile_ >= headerBytes) {
584 frameOffsetInFile_ -= headerBytes;
585 recordOffsetInFrame_ = headerBytes;
586 }
587 auto need{static_cast<std::size_t>(
588 recordOffsetInFrame_ + sizeof header + *recordLength)};
589 got = ReadFrame(frameOffsetInFile_, need, handler);
590 RUNTIME_CHECK(handler, got >= need);
591 std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
592 RUNTIME_CHECK(handler, header == *recordLength);
peter klausler3b635712020-02-13 14:41:56 -0800593}
594
595// There's no portable memrchr(), unfortunately, and strrchr() would
596// fail on a record with a NUL, so we have to do it the hard way.
597static const char *FindLastNewline(const char *str, std::size_t length) {
598 for (const char *p{str + length}; p-- > str;) {
599 if (*p == '\n') {
600 return p;
601 }
602 }
603 return nullptr;
604}
605
peter klausler00063542020-07-03 12:38:22 -0700606void ExternalFileUnit::BackspaceVariableFormattedRecord(
peter klausler3b635712020-02-13 14:41:56 -0800607 IoErrorHandler &handler) {
peter klausler00063542020-07-03 12:38:22 -0700608 // File offset of previous record's newline
609 auto prevNL{
610 frameOffsetInFile_ + static_cast<std::int64_t>(recordOffsetInFrame_) - 1};
611 if (prevNL < 0) {
612 handler.SignalError(IostatBackspaceAtFirstRecord);
613 return;
614 }
615 while (true) {
616 if (frameOffsetInFile_ < prevNL) {
peter klausler3b635712020-02-13 14:41:56 -0800617 if (const char *p{
peter klausler00063542020-07-03 12:38:22 -0700618 FindLastNewline(Frame(), prevNL - 1 - frameOffsetInFile_)}) {
peter klausler3b635712020-02-13 14:41:56 -0800619 recordOffsetInFrame_ = p - Frame() + 1;
peter klausler00063542020-07-03 12:38:22 -0700620 *recordLength = prevNL - (frameOffsetInFile_ + recordOffsetInFrame_);
peter klausler3b635712020-02-13 14:41:56 -0800621 break;
622 }
peter klausler3b635712020-02-13 14:41:56 -0800623 }
peter klausler00063542020-07-03 12:38:22 -0700624 if (frameOffsetInFile_ == 0) {
625 recordOffsetInFrame_ = 0;
626 *recordLength = prevNL;
627 break;
628 }
629 frameOffsetInFile_ -= std::min<std::int64_t>(frameOffsetInFile_, 1024);
630 auto need{static_cast<std::size_t>(prevNL + 1 - frameOffsetInFile_)};
631 auto got{ReadFrame(frameOffsetInFile_, need, handler)};
632 RUNTIME_CHECK(handler, got >= need);
peter klausler3b635712020-02-13 14:41:56 -0800633 }
peter klausler3b635712020-02-13 14:41:56 -0800634 RUNTIME_CHECK(handler, Frame()[recordOffsetInFrame_ + *recordLength] == '\n');
635 if (*recordLength > 0 &&
636 Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') {
637 --*recordLength;
638 }
peter klauslerf7be2512020-01-23 16:59:27 -0800639}
peter klausler00063542020-07-03 12:38:22 -0700640
641void ExternalFileUnit::DoImpliedEndfile(IoErrorHandler &handler) {
642 if (impliedEndfile_) {
643 impliedEndfile_ = false;
644 if (access == Access::Sequential && mayPosition()) {
645 DoEndfile(handler);
646 }
647 }
648}
649
650void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) {
651 endfileRecordNumber = currentRecordNumber;
652 Truncate(frameOffsetInFile_ + recordOffsetInFrame_, handler);
653 BeginRecord();
654 impliedEndfile_ = false;
655}
Tim Keith1f879002020-03-28 21:00:16 -0700656} // namespace Fortran::runtime::io