mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 15:41:46 +00:00
[flang] Implement runtime support for INQUIRE statements
Differential Revision: https://reviews.llvm.org/D85166
This commit is contained in:
parent
ffe0066b62
commit
675ad1bc6a
@ -23,6 +23,23 @@
|
|||||||
|
|
||||||
namespace Fortran::runtime::io {
|
namespace Fortran::runtime::io {
|
||||||
|
|
||||||
|
const char *InquiryKeywordHashDecode(
|
||||||
|
char *buffer, std::size_t n, InquiryKeywordHash hash) {
|
||||||
|
if (n < 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
char *p{buffer + n};
|
||||||
|
*--p = '\0';
|
||||||
|
while (hash > 1) {
|
||||||
|
if (p < buffer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
*--p = 'A' + (hash % 26);
|
||||||
|
hash /= 26;
|
||||||
|
}
|
||||||
|
return hash == 1 ? p : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
template <Direction DIR>
|
template <Direction DIR>
|
||||||
Cookie BeginInternalArrayListIO(const Descriptor &descriptor,
|
Cookie BeginInternalArrayListIO(const Descriptor &descriptor,
|
||||||
void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
|
void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
|
||||||
@ -289,8 +306,8 @@ Cookie IONAME(BeginBackspace)(
|
|||||||
Cookie IONAME(BeginEndfile)(
|
Cookie IONAME(BeginEndfile)(
|
||||||
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||||
Terminator terminator{sourceFile, sourceLine};
|
Terminator terminator{sourceFile, sourceLine};
|
||||||
ExternalFileUnit &unit{
|
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||||
ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
|
unitNumber, Direction::Output, true /*formatted*/, terminator)};
|
||||||
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
||||||
unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
|
unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
|
||||||
}
|
}
|
||||||
@ -298,12 +315,50 @@ Cookie IONAME(BeginEndfile)(
|
|||||||
Cookie IONAME(BeginRewind)(
|
Cookie IONAME(BeginRewind)(
|
||||||
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||||
Terminator terminator{sourceFile, sourceLine};
|
Terminator terminator{sourceFile, sourceLine};
|
||||||
ExternalFileUnit &unit{
|
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||||
ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
|
unitNumber, Direction::Input, true /*formatted*/, terminator)};
|
||||||
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
||||||
unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
|
unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cookie IONAME(BeginInquireUnit)(
|
||||||
|
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||||
|
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
|
||||||
|
return &unit->BeginIoStatement<InquireUnitState>(
|
||||||
|
*unit, sourceFile, sourceLine);
|
||||||
|
} else {
|
||||||
|
// INQUIRE(UNIT=unrecognized unit)
|
||||||
|
Terminator oom{sourceFile, sourceLine};
|
||||||
|
return &New<InquireNoUnitState>{oom}(sourceFile, sourceLine)
|
||||||
|
.release()
|
||||||
|
->ioStatementState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cookie IONAME(BeginInquireFile)(const char *path, std::size_t pathLength,
|
||||||
|
const char *sourceFile, int sourceLine) {
|
||||||
|
Terminator oom{sourceFile, sourceLine};
|
||||||
|
auto trimmed{
|
||||||
|
SaveDefaultCharacter(path, TrimTrailingSpaces(path, pathLength), oom)};
|
||||||
|
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(trimmed.get())}) {
|
||||||
|
// INQUIRE(FILE=) to a connected unit
|
||||||
|
return &unit->BeginIoStatement<InquireUnitState>(
|
||||||
|
*unit, sourceFile, sourceLine);
|
||||||
|
} else {
|
||||||
|
return &New<InquireUnconnectedFileState>{oom}(
|
||||||
|
std::move(trimmed), sourceFile, sourceLine)
|
||||||
|
.release()
|
||||||
|
->ioStatementState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cookie IONAME(BeginInquireIoLength)(const char *sourceFile, int sourceLine) {
|
||||||
|
Terminator oom{sourceFile, sourceLine};
|
||||||
|
return &New<InquireIOLengthState>{oom}(sourceFile, sourceLine)
|
||||||
|
.release()
|
||||||
|
->ioStatementState();
|
||||||
|
}
|
||||||
|
|
||||||
// Control list items
|
// Control list items
|
||||||
|
|
||||||
void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr,
|
void IONAME(EnableHandlers)(Cookie cookie, bool hasIoStat, bool hasErr,
|
||||||
@ -522,29 +577,21 @@ bool IONAME(SetAccess)(Cookie cookie, const char *keyword, std::size_t length) {
|
|||||||
io.GetIoErrorHandler().Crash(
|
io.GetIoErrorHandler().Crash(
|
||||||
"SetAccess() called when not in an OPEN statement");
|
"SetAccess() called when not in an OPEN statement");
|
||||||
}
|
}
|
||||||
ConnectionState &connection{open->GetConnectionState()};
|
|
||||||
Access access{connection.access};
|
|
||||||
static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr};
|
static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr};
|
||||||
switch (IdentifyValue(keyword, length, keywords)) {
|
switch (IdentifyValue(keyword, length, keywords)) {
|
||||||
case 0:
|
case 0:
|
||||||
access = Access::Sequential;
|
open->set_access(Access::Sequential);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
access = Access::Direct;
|
open->set_access(Access::Direct);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
access = Access::Stream;
|
open->set_access(Access::Stream);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
|
open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
|
||||||
static_cast<int>(length), keyword);
|
static_cast<int>(length), keyword);
|
||||||
}
|
}
|
||||||
if (access != connection.access) {
|
|
||||||
if (open->wasExtant()) {
|
|
||||||
open->SignalError("ACCESS= may not be changed on an open unit");
|
|
||||||
}
|
|
||||||
connection.access = access;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,25 +708,18 @@ bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
|
|||||||
io.GetIoErrorHandler().Crash(
|
io.GetIoErrorHandler().Crash(
|
||||||
"SetEncoding() called when not in an OPEN statement");
|
"SetEncoding() called when not in an OPEN statement");
|
||||||
}
|
}
|
||||||
bool isUnformatted{false};
|
|
||||||
static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr};
|
static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr};
|
||||||
switch (IdentifyValue(keyword, length, keywords)) {
|
switch (IdentifyValue(keyword, length, keywords)) {
|
||||||
case 0:
|
case 0:
|
||||||
isUnformatted = false;
|
open->set_isUnformatted(false);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
isUnformatted = true;
|
open->set_isUnformatted(true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'",
|
open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'",
|
||||||
static_cast<int>(length), keyword);
|
static_cast<int>(length), keyword);
|
||||||
}
|
}
|
||||||
if (isUnformatted != open->unit().isUnformatted) {
|
|
||||||
if (open->wasExtant()) {
|
|
||||||
open->SignalError("FORM= may not be changed on an open unit");
|
|
||||||
}
|
|
||||||
open->unit().isUnformatted = isUnformatted;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,11 +817,10 @@ bool IONAME(SetStatus)(Cookie cookie, const char *keyword, std::size_t length) {
|
|||||||
"SetStatus() called when not in an OPEN or CLOSE statement");
|
"SetStatus() called when not in an OPEN or CLOSE statement");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IONAME(SetFile)(
|
bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) {
|
||||||
Cookie cookie, const char *path, std::size_t chars, int kind) {
|
|
||||||
IoStatementState &io{*cookie};
|
IoStatementState &io{*cookie};
|
||||||
if (auto *open{io.get_if<OpenStatementState>()}) {
|
if (auto *open{io.get_if<OpenStatementState>()}) {
|
||||||
open->set_path(path, chars, kind);
|
open->set_path(path, chars);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
io.GetIoErrorHandler().Crash(
|
io.GetIoErrorHandler().Crash(
|
||||||
@ -789,7 +828,8 @@ bool IONAME(SetFile)(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SetInteger(int &x, int kind, int value) {
|
template <typename INT>
|
||||||
|
static bool SetInteger(INT &x, int kind, std::int64_t value) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case 1:
|
case 1:
|
||||||
reinterpret_cast<std::int8_t &>(x) = value;
|
reinterpret_cast<std::int8_t &>(x) = value;
|
||||||
@ -798,7 +838,7 @@ static bool SetInteger(int &x, int kind, int value) {
|
|||||||
reinterpret_cast<std::int16_t &>(x) = value;
|
reinterpret_cast<std::int16_t &>(x) = value;
|
||||||
return true;
|
return true;
|
||||||
case 4:
|
case 4:
|
||||||
x = value;
|
reinterpret_cast<std::int32_t &>(x) = value;
|
||||||
return true;
|
return true;
|
||||||
case 8:
|
case 8:
|
||||||
reinterpret_cast<std::int64_t &>(x) = value;
|
reinterpret_cast<std::int64_t &>(x) = value;
|
||||||
@ -1059,6 +1099,34 @@ void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IONAME(InquireCharacter)(Cookie cookie, InquiryKeywordHash inquiry,
|
||||||
|
char *result, std::size_t length) {
|
||||||
|
IoStatementState &io{*cookie};
|
||||||
|
return io.Inquire(inquiry, result, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IONAME(InquireLogical)(
|
||||||
|
Cookie cookie, InquiryKeywordHash inquiry, bool &result) {
|
||||||
|
IoStatementState &io{*cookie};
|
||||||
|
return io.Inquire(inquiry, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IONAME(InquirePendingId)(Cookie cookie, std::int64_t id, bool &result) {
|
||||||
|
IoStatementState &io{*cookie};
|
||||||
|
return io.Inquire(HashInquiryKeyword("PENDING"), id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IONAME(InquireInteger64)(
|
||||||
|
Cookie cookie, InquiryKeywordHash inquiry, std::int64_t &result, int kind) {
|
||||||
|
IoStatementState &io{*cookie};
|
||||||
|
std::int64_t n;
|
||||||
|
if (io.Inquire(inquiry, n)) {
|
||||||
|
SetInteger(result, kind, n);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
enum Iostat IONAME(EndIoStatement)(Cookie cookie) {
|
enum Iostat IONAME(EndIoStatement)(Cookie cookie) {
|
||||||
IoStatementState &io{*cookie};
|
IoStatementState &io{*cookie};
|
||||||
return static_cast<enum Iostat>(io.EndIoStatement());
|
return static_cast<enum Iostat>(io.EndIoStatement());
|
||||||
|
@ -29,6 +29,26 @@ using ExternalUnit = int;
|
|||||||
using AsynchronousId = int;
|
using AsynchronousId = int;
|
||||||
static constexpr ExternalUnit DefaultUnit{-1}; // READ(*), WRITE(*), PRINT
|
static constexpr ExternalUnit DefaultUnit{-1}; // READ(*), WRITE(*), PRINT
|
||||||
|
|
||||||
|
// INQUIRE specifiers are encoded as simple base-26 packings of
|
||||||
|
// the spellings of their keywords.
|
||||||
|
using InquiryKeywordHash = std::uint64_t;
|
||||||
|
constexpr InquiryKeywordHash HashInquiryKeyword(const char *p) {
|
||||||
|
InquiryKeywordHash hash{1};
|
||||||
|
while (char ch{*p++}) {
|
||||||
|
std::uint64_t letter{0};
|
||||||
|
if (ch >= 'a' && ch <= 'z') {
|
||||||
|
letter = ch - 'a';
|
||||||
|
} else {
|
||||||
|
letter = ch - 'A';
|
||||||
|
}
|
||||||
|
hash = 26 * hash + letter;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *InquiryKeywordHashDecode(
|
||||||
|
char *buffer, std::size_t, InquiryKeywordHash);
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
#define IONAME(name) RTNAME(io##name)
|
#define IONAME(name) RTNAME(io##name)
|
||||||
@ -150,7 +170,7 @@ Cookie IONAME(BeginOpenNewUnit)(
|
|||||||
// BeginInquireIoLength() is basically a no-op output statement.
|
// BeginInquireIoLength() is basically a no-op output statement.
|
||||||
Cookie IONAME(BeginInquireUnit)(
|
Cookie IONAME(BeginInquireUnit)(
|
||||||
ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
|
ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
|
||||||
Cookie IONAME(BeginInquireFile)(const char *, std::size_t, int kind = 1,
|
Cookie IONAME(BeginInquireFile)(const char *, std::size_t,
|
||||||
const char *sourceFile = nullptr, int sourceLine = 0);
|
const char *sourceFile = nullptr, int sourceLine = 0);
|
||||||
Cookie IONAME(BeginInquireIoLength)(
|
Cookie IONAME(BeginInquireIoLength)(
|
||||||
const char *sourceFile = nullptr, int sourceLine = 0);
|
const char *sourceFile = nullptr, int sourceLine = 0);
|
||||||
@ -255,10 +275,7 @@ bool IONAME(SetRecl)(Cookie, std::size_t); // RECL=
|
|||||||
// For CLOSE: STATUS=KEEP, DELETE
|
// For CLOSE: STATUS=KEEP, DELETE
|
||||||
bool IONAME(SetStatus)(Cookie, const char *, std::size_t);
|
bool IONAME(SetStatus)(Cookie, const char *, std::size_t);
|
||||||
|
|
||||||
// SetFile() may pass a CHARACTER argument of non-default kind,
|
bool IONAME(SetFile)(Cookie, const char *, std::size_t chars);
|
||||||
// and such filenames are converted to UTF-8 before being
|
|
||||||
// presented to the filesystem.
|
|
||||||
bool IONAME(SetFile)(Cookie, const char *, std::size_t chars, int kind = 1);
|
|
||||||
|
|
||||||
// Acquires the runtime-created unit number for OPEN(NEWUNIT=)
|
// Acquires the runtime-created unit number for OPEN(NEWUNIT=)
|
||||||
bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4);
|
bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4);
|
||||||
@ -275,18 +292,17 @@ void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG=
|
|||||||
|
|
||||||
// INQUIRE() specifiers are mostly identified by their NUL-terminated
|
// INQUIRE() specifiers are mostly identified by their NUL-terminated
|
||||||
// case-insensitive names.
|
// case-insensitive names.
|
||||||
// ACCESS, ACTION, ASYNCHRONOUS, BLANK, DECIMAL, DELIM, DIRECT, ENCODING,
|
// ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT,
|
||||||
// FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
|
// ENCODING, FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
|
||||||
// SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE:
|
// SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE:
|
||||||
bool IONAME(InquireCharacter)(
|
bool IONAME(InquireCharacter)(Cookie, InquiryKeywordHash, char *, std::size_t);
|
||||||
Cookie, const char *specifier, char *, std::size_t);
|
|
||||||
// EXIST, NAMED, OPENED, and PENDING (without ID):
|
// EXIST, NAMED, OPENED, and PENDING (without ID):
|
||||||
bool IONAME(InquireLogical)(Cookie, const char *specifier, bool &);
|
bool IONAME(InquireLogical)(Cookie, InquiryKeywordHash, bool &);
|
||||||
// PENDING with ID
|
// PENDING with ID
|
||||||
bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &);
|
bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &);
|
||||||
// NEXTREC, NUMBER, POS, RECL, SIZE
|
// NEXTREC, NUMBER, POS, RECL, SIZE
|
||||||
bool IONAME(InquireInteger64)(
|
bool IONAME(InquireInteger64)(
|
||||||
Cookie, const char *specifier, std::int64_t &, int kind = 8);
|
Cookie, InquiryKeywordHash, std::int64_t &, int kind = 8);
|
||||||
|
|
||||||
// This function must be called to end an I/O statement, and its
|
// This function must be called to end an I/O statement, and its
|
||||||
// cookie value may not be used afterwards unless it is recycled
|
// cookie value may not be used afterwards unless it is recycled
|
||||||
|
@ -38,7 +38,7 @@ public:
|
|||||||
|
|
||||||
void SignalError(int iostatOrErrno, const char *msg, ...);
|
void SignalError(int iostatOrErrno, const char *msg, ...);
|
||||||
void SignalError(int iostatOrErrno);
|
void SignalError(int iostatOrErrno);
|
||||||
template <typename... X> void SignalError(const char *msg, X &&... xs) {
|
template <typename... X> void SignalError(const char *msg, X &&...xs) {
|
||||||
SignalError(IostatGenericError, msg, std::forward<X>(xs)...);
|
SignalError(IostatGenericError, msg, std::forward<X>(xs)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,37 @@ std::optional<DataEdit> IoStatementBase::GetNextDataEdit(
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IoStatementBase::Inquire(InquiryKeywordHash, char *, std::size_t) {
|
||||||
|
Crash(
|
||||||
|
"IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IoStatementBase::Inquire(InquiryKeywordHash, bool &) {
|
||||||
|
Crash(
|
||||||
|
"IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t, bool &) {
|
||||||
|
Crash(
|
||||||
|
"IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) {
|
||||||
|
Crash(
|
||||||
|
"IoStatementBase::Inquire() called for I/O statement other than INQUIRE");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry) {
|
||||||
|
char buffer[16];
|
||||||
|
const char *decode{InquiryKeywordHashDecode(buffer, sizeof buffer, inquiry)};
|
||||||
|
Crash("bad InquiryKeywordHash 0x%x (%s)", inquiry,
|
||||||
|
decode ? decode : "(cannot decode)");
|
||||||
|
}
|
||||||
|
|
||||||
template <Direction DIR, typename CHAR>
|
template <Direction DIR, typename CHAR>
|
||||||
InternalIoStatementState<DIR, CHAR>::InternalIoStatementState(
|
InternalIoStatementState<DIR, CHAR>::InternalIoStatementState(
|
||||||
Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)
|
Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)
|
||||||
@ -151,14 +182,9 @@ int ExternalIoStatementBase::EndIoStatement() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenStatementState::set_path(
|
void OpenStatementState::set_path(const char *path, std::size_t length) {
|
||||||
const char *path, std::size_t length, int kind) {
|
pathLength_ = TrimTrailingSpaces(path, length);
|
||||||
if (kind != 1) { // TODO
|
path_ = SaveDefaultCharacter(path, pathLength_, *this);
|
||||||
Crash("OPEN: FILE= with unimplemented: CHARACTER(KIND=%d)", kind);
|
|
||||||
}
|
|
||||||
std::size_t bytes{length * kind}; // TODO: UTF-8 encoding of Unicode path
|
|
||||||
path_ = SaveDefaultCharacter(path, bytes, *this);
|
|
||||||
pathLength_ = length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int OpenStatementState::EndIoStatement() {
|
int OpenStatementState::EndIoStatement() {
|
||||||
@ -166,8 +192,31 @@ int OpenStatementState::EndIoStatement() {
|
|||||||
SignalError("OPEN statement for connected unit may not have STATUS= other "
|
SignalError("OPEN statement for connected unit may not have STATUS= other "
|
||||||
"than 'OLD'");
|
"than 'OLD'");
|
||||||
}
|
}
|
||||||
unit().OpenUnit(status_.value_or(OpenStatus::Unknown), action_, position_,
|
if (path_.get() || wasExtant_ ||
|
||||||
std::move(path_), pathLength_, convert_, *this);
|
(status_ && *status_ == OpenStatus::Scratch)) {
|
||||||
|
unit().OpenUnit(status_.value_or(OpenStatus::Unknown), action_, position_,
|
||||||
|
std::move(path_), pathLength_, convert_, *this);
|
||||||
|
} else {
|
||||||
|
unit().OpenAnonymousUnit(status_.value_or(OpenStatus::Unknown), action_,
|
||||||
|
position_, convert_, *this);
|
||||||
|
}
|
||||||
|
if (access_) {
|
||||||
|
if (*access_ != unit().access) {
|
||||||
|
if (wasExtant_) {
|
||||||
|
SignalError("ACCESS= may not be changed on an open unit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unit().access = *access_;
|
||||||
|
}
|
||||||
|
if (!isUnformatted_) {
|
||||||
|
isUnformatted_ = unit().access != Access::Sequential;
|
||||||
|
}
|
||||||
|
if (*isUnformatted_ != unit().isUnformatted) {
|
||||||
|
if (wasExtant_) {
|
||||||
|
SignalError("FORM= may not be changed on an open unit");
|
||||||
|
}
|
||||||
|
unit().isUnformatted = *isUnformatted_;
|
||||||
|
}
|
||||||
return ExternalIoStatementBase::EndIoStatement();
|
return ExternalIoStatementBase::EndIoStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +227,7 @@ int CloseStatementState::EndIoStatement() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NoopCloseStatementState::EndIoStatement() {
|
int NoUnitIoStatementState::EndIoStatement() {
|
||||||
auto result{IoStatementBase::EndIoStatement()};
|
auto result{IoStatementBase::EndIoStatement()};
|
||||||
FreeMemory(this);
|
FreeMemory(this);
|
||||||
return result;
|
return result;
|
||||||
@ -454,6 +503,26 @@ bool ListDirectedStatementState<Direction::Output>::NeedAdvance(
|
|||||||
width > connection.RemainingSpaceInRecord();
|
width > connection.RemainingSpaceInRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IoStatementState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, char *out, std::size_t chars) {
|
||||||
|
return std::visit(
|
||||||
|
[&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {
|
||||||
|
return std::visit([&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IoStatementState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t id, bool &out) {
|
||||||
|
return std::visit(
|
||||||
|
[&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
|
||||||
|
return std::visit([&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
|
||||||
|
}
|
||||||
|
|
||||||
bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
|
bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
|
||||||
IoStatementState &io, std::size_t length, bool isCharacter) {
|
IoStatementState &io, std::size_t length, bool isCharacter) {
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
@ -678,4 +747,419 @@ int ExternalMiscIoStatementState::EndIoStatement() {
|
|||||||
return ExternalIoStatementBase::EndIoStatement();
|
return ExternalIoStatementBase::EndIoStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InquireUnitState::InquireUnitState(
|
||||||
|
ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
|
||||||
|
: ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
|
||||||
|
|
||||||
|
bool InquireUnitState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, char *result, std::size_t length) {
|
||||||
|
const char *str{nullptr};
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("ACCESS"):
|
||||||
|
switch (unit().access) {
|
||||||
|
case Access::Sequential:
|
||||||
|
str = "SEQUENTIAL";
|
||||||
|
break;
|
||||||
|
case Access::Direct:
|
||||||
|
str = "DIRECT";
|
||||||
|
break;
|
||||||
|
case Access::Stream:
|
||||||
|
str = "STREAM";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("ACTION"):
|
||||||
|
str = unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" : "READ";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("ASYNCHRONOUS"):
|
||||||
|
str = unit().mayAsynchronous() ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("BLANK"):
|
||||||
|
str = unit().isUnformatted ? "UNDEFINED"
|
||||||
|
: unit().modes.editingFlags & blankZero ? "ZERO"
|
||||||
|
: "NULL";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("CONVERT"):
|
||||||
|
str = unit().swapEndianness() ? "SWAP" : "NATIVE";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("DECIMAL"):
|
||||||
|
str = unit().isUnformatted ? "UNDEFINED"
|
||||||
|
: unit().modes.editingFlags & decimalComma ? "COMMA"
|
||||||
|
: "POINT";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("DELIM"):
|
||||||
|
if (unit().isUnformatted) {
|
||||||
|
str = "UNDEFINED";
|
||||||
|
} else {
|
||||||
|
switch (unit().modes.delim) {
|
||||||
|
case '\'':
|
||||||
|
str = "APOSTROPHE";
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
str = "QUOTE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "NONE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("DIRECT"):
|
||||||
|
str = unit().mayPosition() ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("ENCODING"):
|
||||||
|
str = unit().isUnformatted ? "UNDEFINED"
|
||||||
|
: unit().isUTF8 ? "UTF-8"
|
||||||
|
: "ASCII";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("FORM"):
|
||||||
|
str = unit().isUnformatted ? "UNFORMATTED" : "FORMATTED";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("FORMATTED"):
|
||||||
|
str = "YES";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("NAME"):
|
||||||
|
str = unit().path();
|
||||||
|
if (!str) {
|
||||||
|
return true; // result is undefined
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("PAD"):
|
||||||
|
str = unit().isUnformatted ? "UNDEFINED" : unit().modes.pad ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("POSITION"):
|
||||||
|
if (unit().access == Access::Direct) {
|
||||||
|
str = "UNDEFINED";
|
||||||
|
} else {
|
||||||
|
auto size{unit().knownSize()};
|
||||||
|
auto pos{unit().position()};
|
||||||
|
if (pos == size.value_or(pos + 1)) {
|
||||||
|
str = "APPEND";
|
||||||
|
} else if (pos == 0) {
|
||||||
|
str = "REWIND";
|
||||||
|
} else {
|
||||||
|
str = "ASIS"; // processor-dependent & no common behavior
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("READ"):
|
||||||
|
str = unit().mayRead() ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("READWRITE"):
|
||||||
|
str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("ROUND"):
|
||||||
|
if (unit().isUnformatted) {
|
||||||
|
str = "UNDEFINED";
|
||||||
|
} else {
|
||||||
|
switch (unit().modes.round) {
|
||||||
|
case decimal::FortranRounding::RoundNearest:
|
||||||
|
str = "NEAREST";
|
||||||
|
break;
|
||||||
|
case decimal::FortranRounding::RoundUp:
|
||||||
|
str = "UP";
|
||||||
|
break;
|
||||||
|
case decimal::FortranRounding::RoundDown:
|
||||||
|
str = "DOWN";
|
||||||
|
break;
|
||||||
|
case decimal::FortranRounding::RoundToZero:
|
||||||
|
str = "ZERO";
|
||||||
|
break;
|
||||||
|
case decimal::FortranRounding::RoundCompatible:
|
||||||
|
str = "COMPATIBLE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("SEQUENTIAL"):
|
||||||
|
str = "YES";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("SIGN"):
|
||||||
|
str = unit().isUnformatted ? "UNDEFINED"
|
||||||
|
: unit().modes.editingFlags & signPlus ? "PLUS"
|
||||||
|
: "SUPPRESS";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("STREAM"):
|
||||||
|
str = "YES";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("WRITE"):
|
||||||
|
str = unit().mayWrite() ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("UNFORMATTED"):
|
||||||
|
str = "YES";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (str) {
|
||||||
|
ToFortranDefaultCharacter(result, length, str);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("EXIST"):
|
||||||
|
result = true;
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("NAMED"):
|
||||||
|
result = unit().path() != nullptr;
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("OPENED"):
|
||||||
|
result = true;
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("PENDING"):
|
||||||
|
result = false; // asynchronous I/O is not implemented
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireUnitState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t, bool &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("PENDING"):
|
||||||
|
result = false; // asynchronous I/O is not implemented
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireUnitState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("NEXTREC"):
|
||||||
|
if (unit().access == Access::Direct) {
|
||||||
|
result = unit().currentRecordNumber;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("NUMBER"):
|
||||||
|
result = unit().unitNumber();
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("POS"):
|
||||||
|
result = unit().position();
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("RECL"):
|
||||||
|
if (unit().access == Access::Stream) {
|
||||||
|
result = -2;
|
||||||
|
} else if (unit().isFixedRecordLength && unit().recordLength) {
|
||||||
|
result = *unit().recordLength;
|
||||||
|
} else {
|
||||||
|
result = std::numeric_limits<std::uint32_t>::max();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("SIZE"):
|
||||||
|
if (auto size{unit().knownSize()}) {
|
||||||
|
result = *size;
|
||||||
|
} else {
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InquireNoUnitState::InquireNoUnitState(const char *sourceFile, int sourceLine)
|
||||||
|
: NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
|
||||||
|
|
||||||
|
bool InquireNoUnitState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, char *result, std::size_t length) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("ACCESS"):
|
||||||
|
case HashInquiryKeyword("ACTION"):
|
||||||
|
case HashInquiryKeyword("ASYNCHRONOUS"):
|
||||||
|
case HashInquiryKeyword("BLANK"):
|
||||||
|
case HashInquiryKeyword("CONVERT"):
|
||||||
|
case HashInquiryKeyword("DECIMAL"):
|
||||||
|
case HashInquiryKeyword("DELIM"):
|
||||||
|
case HashInquiryKeyword("FORM"):
|
||||||
|
case HashInquiryKeyword("NAME"):
|
||||||
|
case HashInquiryKeyword("PAD"):
|
||||||
|
case HashInquiryKeyword("POSITION"):
|
||||||
|
case HashInquiryKeyword("ROUND"):
|
||||||
|
case HashInquiryKeyword("SIGN"):
|
||||||
|
ToFortranDefaultCharacter(result, length, "UNDEFINED");
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("DIRECT"):
|
||||||
|
case HashInquiryKeyword("ENCODING"):
|
||||||
|
case HashInquiryKeyword("FORMATTED"):
|
||||||
|
case HashInquiryKeyword("READ"):
|
||||||
|
case HashInquiryKeyword("READWRITE"):
|
||||||
|
case HashInquiryKeyword("SEQUENTIAL"):
|
||||||
|
case HashInquiryKeyword("STREAM"):
|
||||||
|
case HashInquiryKeyword("WRITE"):
|
||||||
|
case HashInquiryKeyword("UNFORMATTED"):
|
||||||
|
ToFortranDefaultCharacter(result, length, "UNKNONN");
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("EXIST"):
|
||||||
|
result = true;
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("NAMED"):
|
||||||
|
case HashInquiryKeyword("OPENED"):
|
||||||
|
case HashInquiryKeyword("PENDING"):
|
||||||
|
result = false;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireNoUnitState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t, bool &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("PENDING"):
|
||||||
|
result = false;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireNoUnitState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("NEXTREC"):
|
||||||
|
case HashInquiryKeyword("NUMBER"):
|
||||||
|
case HashInquiryKeyword("POS"):
|
||||||
|
case HashInquiryKeyword("RECL"):
|
||||||
|
case HashInquiryKeyword("SIZE"):
|
||||||
|
result = -1;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InquireUnconnectedFileState::InquireUnconnectedFileState(
|
||||||
|
OwningPtr<char> &&path, const char *sourceFile, int sourceLine)
|
||||||
|
: NoUnitIoStatementState{sourceFile, sourceLine, *this}, path_{std::move(
|
||||||
|
path)} {}
|
||||||
|
|
||||||
|
bool InquireUnconnectedFileState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, char *result, std::size_t length) {
|
||||||
|
const char *str{nullptr};
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("ACCESS"):
|
||||||
|
case HashInquiryKeyword("ACTION"):
|
||||||
|
case HashInquiryKeyword("ASYNCHRONOUS"):
|
||||||
|
case HashInquiryKeyword("BLANK"):
|
||||||
|
case HashInquiryKeyword("CONVERT"):
|
||||||
|
case HashInquiryKeyword("DECIMAL"):
|
||||||
|
case HashInquiryKeyword("DELIM"):
|
||||||
|
case HashInquiryKeyword("FORM"):
|
||||||
|
case HashInquiryKeyword("PAD"):
|
||||||
|
case HashInquiryKeyword("POSITION"):
|
||||||
|
case HashInquiryKeyword("ROUND"):
|
||||||
|
case HashInquiryKeyword("SIGN"):
|
||||||
|
str = "UNDEFINED";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("DIRECT"):
|
||||||
|
case HashInquiryKeyword("ENCODING"):
|
||||||
|
str = "UNKNONN";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("READ"):
|
||||||
|
str = MayRead(path_.get()) ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("READWRITE"):
|
||||||
|
str = MayReadAndWrite(path_.get()) ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("WRITE"):
|
||||||
|
str = MayWrite(path_.get()) ? "YES" : "NO";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("FORMATTED"):
|
||||||
|
case HashInquiryKeyword("SEQUENTIAL"):
|
||||||
|
case HashInquiryKeyword("STREAM"):
|
||||||
|
case HashInquiryKeyword("UNFORMATTED"):
|
||||||
|
str = "YES";
|
||||||
|
break;
|
||||||
|
case HashInquiryKeyword("NAME"):
|
||||||
|
str = path_.get();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (str) {
|
||||||
|
ToFortranDefaultCharacter(result, length, str);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireUnconnectedFileState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, bool &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("EXIST"):
|
||||||
|
result = IsExtant(path_.get());
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("NAMED"):
|
||||||
|
result = true;
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("OPENED"):
|
||||||
|
result = false;
|
||||||
|
return true;
|
||||||
|
case HashInquiryKeyword("PENDING"):
|
||||||
|
result = false;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireUnconnectedFileState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t, bool &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("PENDING"):
|
||||||
|
result = false;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InquireUnconnectedFileState::Inquire(
|
||||||
|
InquiryKeywordHash inquiry, std::int64_t &result) {
|
||||||
|
switch (inquiry) {
|
||||||
|
case HashInquiryKeyword("NEXTREC"):
|
||||||
|
case HashInquiryKeyword("NUMBER"):
|
||||||
|
case HashInquiryKeyword("POS"):
|
||||||
|
case HashInquiryKeyword("RECL"):
|
||||||
|
case HashInquiryKeyword("SIZE"):
|
||||||
|
result = -1;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
BadInquiryKeywordHashCrash(inquiry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InquireIOLengthState::InquireIOLengthState(
|
||||||
|
const char *sourceFile, int sourceLine)
|
||||||
|
: NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
|
||||||
|
|
||||||
|
bool InquireIOLengthState::Emit(
|
||||||
|
const char *, std::size_t n, std::size_t /*elementBytes*/) {
|
||||||
|
bytes_ += n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Fortran::runtime::io
|
} // namespace Fortran::runtime::io
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "internal-unit.h"
|
#include "internal-unit.h"
|
||||||
|
#include "io-api.h"
|
||||||
#include "io-error.h"
|
#include "io-error.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@ -26,6 +27,11 @@ namespace Fortran::runtime::io {
|
|||||||
class ExternalFileUnit;
|
class ExternalFileUnit;
|
||||||
|
|
||||||
class OpenStatementState;
|
class OpenStatementState;
|
||||||
|
class InquireUnitState;
|
||||||
|
class InquireNoUnitState;
|
||||||
|
class InquireUnconnectedFileState;
|
||||||
|
class InquireIOLengthState;
|
||||||
|
class ExternalMiscIoStatementState;
|
||||||
class CloseStatementState;
|
class CloseStatementState;
|
||||||
class NoopCloseStatementState;
|
class NoopCloseStatementState;
|
||||||
|
|
||||||
@ -36,7 +42,6 @@ template <Direction, typename CHAR = char>
|
|||||||
class ExternalFormattedIoStatementState;
|
class ExternalFormattedIoStatementState;
|
||||||
template <Direction> class ExternalListIoStatementState;
|
template <Direction> class ExternalListIoStatementState;
|
||||||
template <Direction> class UnformattedIoStatementState;
|
template <Direction> class UnformattedIoStatementState;
|
||||||
class ExternalMiscIoStatementState;
|
|
||||||
|
|
||||||
// The Cookie type in the I/O API is a pointer (for C) to this class.
|
// The Cookie type in the I/O API is a pointer (for C) to this class.
|
||||||
class IoStatementState {
|
class IoStatementState {
|
||||||
@ -60,6 +65,10 @@ public:
|
|||||||
ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
|
ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
|
||||||
MutableModes &mutableModes();
|
MutableModes &mutableModes();
|
||||||
void BeginReadingRecord();
|
void BeginReadingRecord();
|
||||||
|
bool Inquire(InquiryKeywordHash, char *, std::size_t);
|
||||||
|
bool Inquire(InquiryKeywordHash, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING=
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t &);
|
||||||
|
|
||||||
// N.B.: this also works with base classes
|
// N.B.: this also works with base classes
|
||||||
template <typename A> A *get_if() const {
|
template <typename A> A *get_if() const {
|
||||||
@ -98,6 +107,10 @@ private:
|
|||||||
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
|
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
|
||||||
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
|
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
|
||||||
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
|
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
|
||||||
|
std::reference_wrapper<InquireUnitState>,
|
||||||
|
std::reference_wrapper<InquireNoUnitState>,
|
||||||
|
std::reference_wrapper<InquireUnconnectedFileState>,
|
||||||
|
std::reference_wrapper<InquireIOLengthState>,
|
||||||
std::reference_wrapper<ExternalMiscIoStatementState>>
|
std::reference_wrapper<ExternalMiscIoStatementState>>
|
||||||
u_;
|
u_;
|
||||||
};
|
};
|
||||||
@ -110,6 +123,12 @@ struct IoStatementBase : public DefaultFormatControlCallbacks {
|
|||||||
std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
|
std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
|
||||||
ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
|
ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
|
||||||
void BeginReadingRecord() {}
|
void BeginReadingRecord() {}
|
||||||
|
|
||||||
|
bool Inquire(InquiryKeywordHash, char *, std::size_t);
|
||||||
|
bool Inquire(InquiryKeywordHash, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t &);
|
||||||
|
void BadInquiryKeywordHashCrash(InquiryKeywordHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputStatementState {};
|
struct InputStatementState {};
|
||||||
@ -303,10 +322,12 @@ public:
|
|||||||
wasExtant} {}
|
wasExtant} {}
|
||||||
bool wasExtant() const { return wasExtant_; }
|
bool wasExtant() const { return wasExtant_; }
|
||||||
void set_status(OpenStatus status) { status_ = status; } // STATUS=
|
void set_status(OpenStatus status) { status_ = status; } // STATUS=
|
||||||
void set_path(const char *, std::size_t, int kind); // FILE=
|
void set_path(const char *, std::size_t); // FILE=
|
||||||
void set_position(Position position) { position_ = position; } // POSITION=
|
void set_position(Position position) { position_ = position; } // POSITION=
|
||||||
void set_action(Action action) { action_ = action; } // ACTION=
|
void set_action(Action action) { action_ = action; } // ACTION=
|
||||||
void set_convert(Convert convert) { convert_ = convert; } // CONVERT=
|
void set_convert(Convert convert) { convert_ = convert; } // CONVERT=
|
||||||
|
void set_access(Access access) { access_ = access; } // ACCESS=
|
||||||
|
void set_isUnformatted(bool yes = true) { isUnformatted_ = yes; } // FORM=
|
||||||
int EndIoStatement();
|
int EndIoStatement();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -317,6 +338,8 @@ private:
|
|||||||
Convert convert_{Convert::Native};
|
Convert convert_{Convert::Native};
|
||||||
OwningPtr<char> path_;
|
OwningPtr<char> path_;
|
||||||
std::size_t pathLength_;
|
std::size_t pathLength_;
|
||||||
|
std::optional<bool> isUnformatted_;
|
||||||
|
std::optional<Access> access_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CloseStatementState : public ExternalIoStatementBase {
|
class CloseStatementState : public ExternalIoStatementBase {
|
||||||
@ -331,21 +354,31 @@ private:
|
|||||||
CloseStatus status_{CloseStatus::Keep};
|
CloseStatus status_{CloseStatus::Keep};
|
||||||
};
|
};
|
||||||
|
|
||||||
class NoopCloseStatementState : public IoStatementBase {
|
// For CLOSE(bad unit) and INQUIRE(unconnected unit)
|
||||||
|
class NoUnitIoStatementState : public IoStatementBase {
|
||||||
public:
|
public:
|
||||||
NoopCloseStatementState(const char *sourceFile, int sourceLine)
|
|
||||||
: IoStatementBase{sourceFile, sourceLine}, ioStatementState_{*this} {}
|
|
||||||
IoStatementState &ioStatementState() { return ioStatementState_; }
|
IoStatementState &ioStatementState() { return ioStatementState_; }
|
||||||
void set_status(CloseStatus) {} // discards
|
|
||||||
MutableModes &mutableModes() { return connection_.modes; }
|
MutableModes &mutableModes() { return connection_.modes; }
|
||||||
ConnectionState &GetConnectionState() { return connection_; }
|
ConnectionState &GetConnectionState() { return connection_; }
|
||||||
int EndIoStatement();
|
int EndIoStatement();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <typename A>
|
||||||
|
NoUnitIoStatementState(const char *sourceFile, int sourceLine, A &stmt)
|
||||||
|
: IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt} {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IoStatementState ioStatementState_; // points to *this
|
IoStatementState ioStatementState_; // points to *this
|
||||||
ConnectionState connection_;
|
ConnectionState connection_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NoopCloseStatementState : public NoUnitIoStatementState {
|
||||||
|
public:
|
||||||
|
NoopCloseStatementState(const char *sourceFile, int sourceLine)
|
||||||
|
: NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
|
||||||
|
void set_status(CloseStatus) {} // discards
|
||||||
|
};
|
||||||
|
|
||||||
extern template class InternalIoStatementState<Direction::Output>;
|
extern template class InternalIoStatementState<Direction::Output>;
|
||||||
extern template class InternalIoStatementState<Direction::Input>;
|
extern template class InternalIoStatementState<Direction::Input>;
|
||||||
extern template class InternalFormattedIoStatementState<Direction::Output>;
|
extern template class InternalFormattedIoStatementState<Direction::Output>;
|
||||||
@ -369,6 +402,49 @@ extern template class FormatControl<
|
|||||||
extern template class FormatControl<
|
extern template class FormatControl<
|
||||||
ExternalFormattedIoStatementState<Direction::Input>>;
|
ExternalFormattedIoStatementState<Direction::Input>>;
|
||||||
|
|
||||||
|
class InquireUnitState : public ExternalIoStatementBase {
|
||||||
|
public:
|
||||||
|
InquireUnitState(ExternalFileUnit &unit, const char *sourceFile = nullptr,
|
||||||
|
int sourceLine = 0);
|
||||||
|
bool Inquire(InquiryKeywordHash, char *, std::size_t);
|
||||||
|
bool Inquire(InquiryKeywordHash, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t &);
|
||||||
|
};
|
||||||
|
|
||||||
|
class InquireNoUnitState : public NoUnitIoStatementState {
|
||||||
|
public:
|
||||||
|
InquireNoUnitState(const char *sourceFile = nullptr, int sourceLine = 0);
|
||||||
|
bool Inquire(InquiryKeywordHash, char *, std::size_t);
|
||||||
|
bool Inquire(InquiryKeywordHash, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t &);
|
||||||
|
};
|
||||||
|
|
||||||
|
class InquireUnconnectedFileState : public NoUnitIoStatementState {
|
||||||
|
public:
|
||||||
|
InquireUnconnectedFileState(OwningPtr<char> &&path,
|
||||||
|
const char *sourceFile = nullptr, int sourceLine = 0);
|
||||||
|
bool Inquire(InquiryKeywordHash, char *, std::size_t);
|
||||||
|
bool Inquire(InquiryKeywordHash, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
|
||||||
|
bool Inquire(InquiryKeywordHash, std::int64_t &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OwningPtr<char> path_; // trimmed and NUL terminated
|
||||||
|
};
|
||||||
|
|
||||||
|
class InquireIOLengthState : public NoUnitIoStatementState,
|
||||||
|
public OutputStatementState {
|
||||||
|
public:
|
||||||
|
InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0);
|
||||||
|
std::size_t bytes() const { return bytes_; }
|
||||||
|
bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t bytes_{0};
|
||||||
|
};
|
||||||
|
|
||||||
class ExternalMiscIoStatementState : public ExternalIoStatementBase {
|
class ExternalMiscIoStatementState : public ExternalIoStatementBase {
|
||||||
public:
|
public:
|
||||||
enum Which { Flush, Backspace, Endfile, Rewind };
|
enum Which { Flush, Backspace, Endfile, Rewind };
|
||||||
|
@ -42,7 +42,7 @@ template <typename A> class SizedNew {
|
|||||||
public:
|
public:
|
||||||
explicit SizedNew(const Terminator &terminator) : terminator_{terminator} {}
|
explicit SizedNew(const Terminator &terminator) : terminator_{terminator} {}
|
||||||
template <typename... X>
|
template <typename... X>
|
||||||
[[nodiscard]] OwningPtr<A> operator()(std::size_t bytes, X &&... x) {
|
[[nodiscard]] OwningPtr<A> operator()(std::size_t bytes, X &&...x) {
|
||||||
return OwningPtr<A>{new (AllocateMemoryOrCrash(terminator_, bytes))
|
return OwningPtr<A>{new (AllocateMemoryOrCrash(terminator_, bytes))
|
||||||
A{std::forward<X>(x)...}};
|
A{std::forward<X>(x)...}};
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ private:
|
|||||||
|
|
||||||
template <typename A> struct New : public SizedNew<A> {
|
template <typename A> struct New : public SizedNew<A> {
|
||||||
using SizedNew<A>::SizedNew;
|
using SizedNew<A>::SizedNew;
|
||||||
template <typename... X> [[nodiscard]] OwningPtr<A> operator()(X &&... x) {
|
template <typename... X> [[nodiscard]] OwningPtr<A> operator()(X &&...x) {
|
||||||
return SizedNew<A>::operator()(sizeof(A), std::forward<X>(x)...);
|
return SizedNew<A>::operator()(sizeof(A), std::forward<X>(x)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,13 @@
|
|||||||
|
|
||||||
namespace Fortran::runtime {
|
namespace Fortran::runtime {
|
||||||
|
|
||||||
|
std::size_t TrimTrailingSpaces(const char *s, std::size_t n) {
|
||||||
|
while (n > 0 && s[n - 1] == ' ') {
|
||||||
|
--n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
OwningPtr<char> SaveDefaultCharacter(
|
OwningPtr<char> SaveDefaultCharacter(
|
||||||
const char *s, std::size_t length, const Terminator &terminator) {
|
const char *s, std::size_t length, const Terminator &terminator) {
|
||||||
if (s) {
|
if (s) {
|
||||||
|
@ -18,6 +18,8 @@ namespace Fortran::runtime {
|
|||||||
|
|
||||||
class Terminator;
|
class Terminator;
|
||||||
|
|
||||||
|
std::size_t TrimTrailingSpaces(const char *, std::size_t);
|
||||||
|
|
||||||
OwningPtr<char> SaveDefaultCharacter(
|
OwningPtr<char> SaveDefaultCharacter(
|
||||||
const char *, std::size_t, const Terminator &);
|
const char *, std::size_t, const Terminator &);
|
||||||
|
|
||||||
|
@ -72,6 +72,20 @@ void UnitMap::FlushAll(IoErrorHandler &handler) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExternalFileUnit *UnitMap::Find(const char *path) {
|
||||||
|
if (path) {
|
||||||
|
// TODO: Faster data structure
|
||||||
|
for (int j{0}; j < buckets_; ++j) {
|
||||||
|
for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) {
|
||||||
|
if (p->unit.path() && std::strcmp(p->unit.path(), path) == 0) {
|
||||||
|
return &p->unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) {
|
ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) {
|
||||||
Chain &chain{*New<Chain>{terminator}(n).release()};
|
Chain &chain{*New<Chain>{terminator}(n).release()};
|
||||||
chain.next.reset(&chain);
|
chain.next.reset(&chain);
|
||||||
|
@ -34,6 +34,12 @@ public:
|
|||||||
return p ? *p : Create(n, terminator);
|
return p ? *p : Create(n, terminator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unit look-up by name is needed for INQUIRE(FILE="...")
|
||||||
|
ExternalFileUnit *LookUp(const char *path) {
|
||||||
|
CriticalSection critical{lock_};
|
||||||
|
return Find(path);
|
||||||
|
}
|
||||||
|
|
||||||
ExternalFileUnit &NewUnit(const Terminator &terminator) {
|
ExternalFileUnit &NewUnit(const Terminator &terminator) {
|
||||||
CriticalSection critical{lock_};
|
CriticalSection critical{lock_};
|
||||||
return Create(nextNewUnit_--, terminator);
|
return Create(nextNewUnit_--, terminator);
|
||||||
@ -72,6 +78,7 @@ private:
|
|||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
ExternalFileUnit *Find(const char *path);
|
||||||
|
|
||||||
ExternalFileUnit &Create(int, const Terminator &);
|
ExternalFileUnit &Create(int, const Terminator &);
|
||||||
|
|
||||||
|
@ -59,20 +59,19 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
|
|||||||
ExternalFileUnit &result{
|
ExternalFileUnit &result{
|
||||||
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
|
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
// I/O to an unconnected unit reads/creates a local file, e.g. fort.7
|
|
||||||
std::size_t pathMaxLen{32};
|
|
||||||
auto path{SizedNew<char>{terminator}(pathMaxLen)};
|
|
||||||
std::snprintf(path.get(), pathMaxLen, "fort.%d", unit);
|
|
||||||
IoErrorHandler handler{terminator};
|
IoErrorHandler handler{terminator};
|
||||||
result.OpenUnit(
|
result.OpenAnonymousUnit(
|
||||||
dir == Direction::Input ? OpenStatus::Old : OpenStatus::Replace,
|
dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
|
||||||
Action::ReadWrite, Position::Rewind, std::move(path),
|
Action::ReadWrite, Position::Rewind, Convert::Native, handler);
|
||||||
std::strlen(path.get()), Convert::Native, handler);
|
|
||||||
result.isUnformatted = isUnformatted;
|
result.isUnformatted = isUnformatted;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExternalFileUnit *ExternalFileUnit::LookUp(const char *path) {
|
||||||
|
return GetUnitMap().LookUp(path);
|
||||||
|
}
|
||||||
|
|
||||||
ExternalFileUnit &ExternalFileUnit::CreateNew(
|
ExternalFileUnit &ExternalFileUnit::CreateNew(
|
||||||
int unit, const Terminator &terminator) {
|
int unit, const Terminator &terminator) {
|
||||||
bool wasExtant{false};
|
bool wasExtant{false};
|
||||||
@ -125,10 +124,7 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
|
|||||||
handler.SignalError(IostatOpenBadRecl,
|
handler.SignalError(IostatOpenBadRecl,
|
||||||
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
|
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
|
||||||
unitNumber(), static_cast<std::intmax_t>(*recordLength));
|
unitNumber(), static_cast<std::intmax_t>(*recordLength));
|
||||||
} else if (!totalBytes) {
|
} else if (totalBytes && (*totalBytes % *recordLength != 0)) {
|
||||||
handler.SignalError(IostatOpenUnknownSize,
|
|
||||||
"OPEN(UNIT=%d,ACCESS='DIRECT'): file size is not known");
|
|
||||||
} else if (*totalBytes % *recordLength != 0) {
|
|
||||||
handler.SignalError(IostatOpenBadAppend,
|
handler.SignalError(IostatOpenBadAppend,
|
||||||
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
|
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
|
||||||
"even divisor of the file size %jd",
|
"even divisor of the file size %jd",
|
||||||
@ -137,7 +133,7 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (position == Position::Append) {
|
if (position == Position::Append) {
|
||||||
if (*totalBytes && recordLength && *recordLength) {
|
if (totalBytes && recordLength && *recordLength) {
|
||||||
endfileRecordNumber = 1 + (*totalBytes / *recordLength);
|
endfileRecordNumber = 1 + (*totalBytes / *recordLength);
|
||||||
} else {
|
} else {
|
||||||
// Fake it so that we can backspace relative from the end
|
// Fake it so that we can backspace relative from the end
|
||||||
@ -149,6 +145,17 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExternalFileUnit::OpenAnonymousUnit(OpenStatus status,
|
||||||
|
std::optional<Action> action, Position position, Convert convert,
|
||||||
|
IoErrorHandler &handler) {
|
||||||
|
// I/O to an unconnected unit reads/creates a local file, e.g. fort.7
|
||||||
|
std::size_t pathMaxLen{32};
|
||||||
|
auto path{SizedNew<char>{handler}(pathMaxLen)};
|
||||||
|
std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_);
|
||||||
|
OpenUnit(status, action, position, std::move(path), std::strlen(path.get()),
|
||||||
|
convert, handler);
|
||||||
|
}
|
||||||
|
|
||||||
void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
|
void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
|
||||||
DoImpliedEndfile(handler);
|
DoImpliedEndfile(handler);
|
||||||
Flush(handler);
|
Flush(handler);
|
||||||
|
@ -35,6 +35,7 @@ class ExternalFileUnit : public ConnectionState,
|
|||||||
public:
|
public:
|
||||||
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {}
|
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {}
|
||||||
int unitNumber() const { return unitNumber_; }
|
int unitNumber() const { return unitNumber_; }
|
||||||
|
bool swapEndianness() const { return swapEndianness_; }
|
||||||
|
|
||||||
static ExternalFileUnit *LookUp(int unit);
|
static ExternalFileUnit *LookUp(int unit);
|
||||||
static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
|
static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
|
||||||
@ -42,6 +43,7 @@ public:
|
|||||||
int unit, const Terminator &, bool &wasExtant);
|
int unit, const Terminator &, bool &wasExtant);
|
||||||
static ExternalFileUnit &LookUpOrCreateAnonymous(
|
static ExternalFileUnit &LookUpOrCreateAnonymous(
|
||||||
int unit, Direction, bool isUnformatted, const Terminator &);
|
int unit, Direction, bool isUnformatted, const Terminator &);
|
||||||
|
static ExternalFileUnit *LookUp(const char *path);
|
||||||
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
|
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
|
||||||
static ExternalFileUnit *LookUpForClose(int unit);
|
static ExternalFileUnit *LookUpForClose(int unit);
|
||||||
static int NewUnit(const Terminator &);
|
static int NewUnit(const Terminator &);
|
||||||
@ -51,13 +53,15 @@ public:
|
|||||||
void OpenUnit(OpenStatus, std::optional<Action>, Position,
|
void OpenUnit(OpenStatus, std::optional<Action>, Position,
|
||||||
OwningPtr<char> &&path, std::size_t pathLength, Convert,
|
OwningPtr<char> &&path, std::size_t pathLength, Convert,
|
||||||
IoErrorHandler &);
|
IoErrorHandler &);
|
||||||
|
void OpenAnonymousUnit(
|
||||||
|
OpenStatus, std::optional<Action>, Position, Convert, IoErrorHandler &);
|
||||||
void CloseUnit(CloseStatus, IoErrorHandler &);
|
void CloseUnit(CloseStatus, IoErrorHandler &);
|
||||||
void DestroyClosed();
|
void DestroyClosed();
|
||||||
|
|
||||||
bool SetDirection(Direction, IoErrorHandler &);
|
bool SetDirection(Direction, IoErrorHandler &);
|
||||||
|
|
||||||
template <typename A, typename... X>
|
template <typename A, typename... X>
|
||||||
IoStatementState &BeginIoStatement(X &&... xs) {
|
IoStatementState &BeginIoStatement(X &&...xs) {
|
||||||
// TODO: Child data transfer statements vs. locking
|
// TODO: Child data transfer statements vs. locking
|
||||||
lock_.Take(); // dropped in EndIoStatement()
|
lock_.Take(); // dropped in EndIoStatement()
|
||||||
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
|
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
|
||||||
@ -111,7 +115,7 @@ private:
|
|||||||
ExternalListIoStatementState<Direction::Output>,
|
ExternalListIoStatementState<Direction::Output>,
|
||||||
ExternalListIoStatementState<Direction::Input>,
|
ExternalListIoStatementState<Direction::Input>,
|
||||||
UnformattedIoStatementState<Direction::Output>,
|
UnformattedIoStatementState<Direction::Output>,
|
||||||
UnformattedIoStatementState<Direction::Input>,
|
UnformattedIoStatementState<Direction::Input>, InquireUnitState,
|
||||||
ExternalMiscIoStatementState>
|
ExternalMiscIoStatementState>
|
||||||
u_;
|
u_;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user