mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 13:50:11 +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 {
|
||||
|
||||
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>
|
||||
Cookie BeginInternalArrayListIO(const Descriptor &descriptor,
|
||||
void ** /*scratchArea*/, std::size_t /*scratchBytes*/,
|
||||
@ -289,8 +306,8 @@ Cookie IONAME(BeginBackspace)(
|
||||
Cookie IONAME(BeginEndfile)(
|
||||
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||
Terminator terminator{sourceFile, sourceLine};
|
||||
ExternalFileUnit &unit{
|
||||
ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, Direction::Output, true /*formatted*/, terminator)};
|
||||
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
||||
unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine);
|
||||
}
|
||||
@ -298,12 +315,50 @@ Cookie IONAME(BeginEndfile)(
|
||||
Cookie IONAME(BeginRewind)(
|
||||
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
|
||||
Terminator terminator{sourceFile, sourceLine};
|
||||
ExternalFileUnit &unit{
|
||||
ExternalFileUnit::LookUpOrCrash(unitNumber, terminator)};
|
||||
ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
unitNumber, Direction::Input, true /*formatted*/, terminator)};
|
||||
return &unit.BeginIoStatement<ExternalMiscIoStatementState>(
|
||||
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
|
||||
|
||||
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(
|
||||
"SetAccess() called when not in an OPEN statement");
|
||||
}
|
||||
ConnectionState &connection{open->GetConnectionState()};
|
||||
Access access{connection.access};
|
||||
static const char *keywords[]{"SEQUENTIAL", "DIRECT", "STREAM", nullptr};
|
||||
switch (IdentifyValue(keyword, length, keywords)) {
|
||||
case 0:
|
||||
access = Access::Sequential;
|
||||
open->set_access(Access::Sequential);
|
||||
break;
|
||||
case 1:
|
||||
access = Access::Direct;
|
||||
open->set_access(Access::Direct);
|
||||
break;
|
||||
case 2:
|
||||
access = Access::Stream;
|
||||
open->set_access(Access::Stream);
|
||||
break;
|
||||
default:
|
||||
open->SignalError(IostatErrorInKeyword, "Invalid ACCESS='%.*s'",
|
||||
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;
|
||||
}
|
||||
|
||||
@ -661,25 +708,18 @@ bool IONAME(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
|
||||
io.GetIoErrorHandler().Crash(
|
||||
"SetEncoding() called when not in an OPEN statement");
|
||||
}
|
||||
bool isUnformatted{false};
|
||||
static const char *keywords[]{"FORMATTED", "UNFORMATTED", nullptr};
|
||||
switch (IdentifyValue(keyword, length, keywords)) {
|
||||
case 0:
|
||||
isUnformatted = false;
|
||||
open->set_isUnformatted(false);
|
||||
break;
|
||||
case 1:
|
||||
isUnformatted = true;
|
||||
open->set_isUnformatted(true);
|
||||
break;
|
||||
default:
|
||||
open->SignalError(IostatErrorInKeyword, "Invalid FORM='%.*s'",
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
bool IONAME(SetFile)(
|
||||
Cookie cookie, const char *path, std::size_t chars, int kind) {
|
||||
bool IONAME(SetFile)(Cookie cookie, const char *path, std::size_t chars) {
|
||||
IoStatementState &io{*cookie};
|
||||
if (auto *open{io.get_if<OpenStatementState>()}) {
|
||||
open->set_path(path, chars, kind);
|
||||
open->set_path(path, chars);
|
||||
return true;
|
||||
}
|
||||
io.GetIoErrorHandler().Crash(
|
||||
@ -789,7 +828,8 @@ bool IONAME(SetFile)(
|
||||
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) {
|
||||
case 1:
|
||||
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;
|
||||
return true;
|
||||
case 4:
|
||||
x = value;
|
||||
reinterpret_cast<std::int32_t &>(x) = value;
|
||||
return true;
|
||||
case 8:
|
||||
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) {
|
||||
IoStatementState &io{*cookie};
|
||||
return static_cast<enum Iostat>(io.EndIoStatement());
|
||||
|
@ -29,6 +29,26 @@ using ExternalUnit = int;
|
||||
using AsynchronousId = int;
|
||||
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" {
|
||||
|
||||
#define IONAME(name) RTNAME(io##name)
|
||||
@ -150,7 +170,7 @@ Cookie IONAME(BeginOpenNewUnit)(
|
||||
// BeginInquireIoLength() is basically a no-op output statement.
|
||||
Cookie IONAME(BeginInquireUnit)(
|
||||
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);
|
||||
Cookie IONAME(BeginInquireIoLength)(
|
||||
const char *sourceFile = nullptr, int sourceLine = 0);
|
||||
@ -255,10 +275,7 @@ bool IONAME(SetRecl)(Cookie, std::size_t); // RECL=
|
||||
// For CLOSE: STATUS=KEEP, DELETE
|
||||
bool IONAME(SetStatus)(Cookie, const char *, std::size_t);
|
||||
|
||||
// SetFile() may pass a CHARACTER argument of non-default kind,
|
||||
// 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);
|
||||
bool IONAME(SetFile)(Cookie, const char *, std::size_t chars);
|
||||
|
||||
// Acquires the runtime-created unit number for OPEN(NEWUNIT=)
|
||||
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
|
||||
// case-insensitive names.
|
||||
// ACCESS, ACTION, ASYNCHRONOUS, BLANK, DECIMAL, DELIM, DIRECT, ENCODING,
|
||||
// FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
|
||||
// ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT,
|
||||
// ENCODING, FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
|
||||
// SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE:
|
||||
bool IONAME(InquireCharacter)(
|
||||
Cookie, const char *specifier, char *, std::size_t);
|
||||
bool IONAME(InquireCharacter)(Cookie, InquiryKeywordHash, char *, std::size_t);
|
||||
// EXIST, NAMED, OPENED, and PENDING (without ID):
|
||||
bool IONAME(InquireLogical)(Cookie, const char *specifier, bool &);
|
||||
bool IONAME(InquireLogical)(Cookie, InquiryKeywordHash, bool &);
|
||||
// PENDING with ID
|
||||
bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &);
|
||||
// NEXTREC, NUMBER, POS, RECL, SIZE
|
||||
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
|
||||
// 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);
|
||||
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)...);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,37 @@ std::optional<DataEdit> IoStatementBase::GetNextDataEdit(
|
||||
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>
|
||||
InternalIoStatementState<DIR, CHAR>::InternalIoStatementState(
|
||||
Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)
|
||||
@ -151,14 +182,9 @@ int ExternalIoStatementBase::EndIoStatement() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void OpenStatementState::set_path(
|
||||
const char *path, std::size_t length, int kind) {
|
||||
if (kind != 1) { // TODO
|
||||
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;
|
||||
void OpenStatementState::set_path(const char *path, std::size_t length) {
|
||||
pathLength_ = TrimTrailingSpaces(path, length);
|
||||
path_ = SaveDefaultCharacter(path, pathLength_, *this);
|
||||
}
|
||||
|
||||
int OpenStatementState::EndIoStatement() {
|
||||
@ -166,8 +192,31 @@ int OpenStatementState::EndIoStatement() {
|
||||
SignalError("OPEN statement for connected unit may not have STATUS= other "
|
||||
"than 'OLD'");
|
||||
}
|
||||
unit().OpenUnit(status_.value_or(OpenStatus::Unknown), action_, position_,
|
||||
std::move(path_), pathLength_, convert_, *this);
|
||||
if (path_.get() || wasExtant_ ||
|
||||
(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();
|
||||
}
|
||||
|
||||
@ -178,7 +227,7 @@ int CloseStatementState::EndIoStatement() {
|
||||
return result;
|
||||
}
|
||||
|
||||
int NoopCloseStatementState::EndIoStatement() {
|
||||
int NoUnitIoStatementState::EndIoStatement() {
|
||||
auto result{IoStatementBase::EndIoStatement()};
|
||||
FreeMemory(this);
|
||||
return result;
|
||||
@ -454,6 +503,26 @@ bool ListDirectedStatementState<Direction::Output>::NeedAdvance(
|
||||
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(
|
||||
IoStatementState &io, std::size_t length, bool isCharacter) {
|
||||
if (length == 0) {
|
||||
@ -678,4 +747,419 @@ int ExternalMiscIoStatementState::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
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "file.h"
|
||||
#include "format.h"
|
||||
#include "internal-unit.h"
|
||||
#include "io-api.h"
|
||||
#include "io-error.h"
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
@ -26,6 +27,11 @@ namespace Fortran::runtime::io {
|
||||
class ExternalFileUnit;
|
||||
|
||||
class OpenStatementState;
|
||||
class InquireUnitState;
|
||||
class InquireNoUnitState;
|
||||
class InquireUnconnectedFileState;
|
||||
class InquireIOLengthState;
|
||||
class ExternalMiscIoStatementState;
|
||||
class CloseStatementState;
|
||||
class NoopCloseStatementState;
|
||||
|
||||
@ -36,7 +42,6 @@ template <Direction, typename CHAR = char>
|
||||
class ExternalFormattedIoStatementState;
|
||||
template <Direction> class ExternalListIoStatementState;
|
||||
template <Direction> class UnformattedIoStatementState;
|
||||
class ExternalMiscIoStatementState;
|
||||
|
||||
// The Cookie type in the I/O API is a pointer (for C) to this class.
|
||||
class IoStatementState {
|
||||
@ -60,6 +65,10 @@ public:
|
||||
ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
|
||||
MutableModes &mutableModes();
|
||||
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
|
||||
template <typename A> A *get_if() const {
|
||||
@ -98,6 +107,10 @@ private:
|
||||
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
|
||||
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
|
||||
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>>
|
||||
u_;
|
||||
};
|
||||
@ -110,6 +123,12 @@ struct IoStatementBase : public DefaultFormatControlCallbacks {
|
||||
std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
|
||||
ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
|
||||
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 {};
|
||||
@ -303,10 +322,12 @@ public:
|
||||
wasExtant} {}
|
||||
bool wasExtant() const { return wasExtant_; }
|
||||
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_action(Action action) { action_ = action; } // ACTION=
|
||||
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();
|
||||
|
||||
private:
|
||||
@ -317,6 +338,8 @@ private:
|
||||
Convert convert_{Convert::Native};
|
||||
OwningPtr<char> path_;
|
||||
std::size_t pathLength_;
|
||||
std::optional<bool> isUnformatted_;
|
||||
std::optional<Access> access_;
|
||||
};
|
||||
|
||||
class CloseStatementState : public ExternalIoStatementBase {
|
||||
@ -331,21 +354,31 @@ private:
|
||||
CloseStatus status_{CloseStatus::Keep};
|
||||
};
|
||||
|
||||
class NoopCloseStatementState : public IoStatementBase {
|
||||
// For CLOSE(bad unit) and INQUIRE(unconnected unit)
|
||||
class NoUnitIoStatementState : public IoStatementBase {
|
||||
public:
|
||||
NoopCloseStatementState(const char *sourceFile, int sourceLine)
|
||||
: IoStatementBase{sourceFile, sourceLine}, ioStatementState_{*this} {}
|
||||
IoStatementState &ioStatementState() { return ioStatementState_; }
|
||||
void set_status(CloseStatus) {} // discards
|
||||
MutableModes &mutableModes() { return connection_.modes; }
|
||||
ConnectionState &GetConnectionState() { return connection_; }
|
||||
int EndIoStatement();
|
||||
|
||||
protected:
|
||||
template <typename A>
|
||||
NoUnitIoStatementState(const char *sourceFile, int sourceLine, A &stmt)
|
||||
: IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt} {}
|
||||
|
||||
private:
|
||||
IoStatementState ioStatementState_; // points to *this
|
||||
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::Input>;
|
||||
extern template class InternalFormattedIoStatementState<Direction::Output>;
|
||||
@ -369,6 +402,49 @@ extern template class FormatControl<
|
||||
extern template class FormatControl<
|
||||
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 {
|
||||
public:
|
||||
enum Which { Flush, Backspace, Endfile, Rewind };
|
||||
|
@ -42,7 +42,7 @@ template <typename A> class SizedNew {
|
||||
public:
|
||||
explicit SizedNew(const Terminator &terminator) : terminator_{terminator} {}
|
||||
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))
|
||||
A{std::forward<X>(x)...}};
|
||||
}
|
||||
@ -53,7 +53,7 @@ private:
|
||||
|
||||
template <typename A> struct New : public SizedNew<A> {
|
||||
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)...);
|
||||
}
|
||||
};
|
||||
|
@ -12,6 +12,13 @@
|
||||
|
||||
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(
|
||||
const char *s, std::size_t length, const Terminator &terminator) {
|
||||
if (s) {
|
||||
|
@ -18,6 +18,8 @@ namespace Fortran::runtime {
|
||||
|
||||
class Terminator;
|
||||
|
||||
std::size_t TrimTrailingSpaces(const char *, std::size_t);
|
||||
|
||||
OwningPtr<char> SaveDefaultCharacter(
|
||||
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) {
|
||||
Chain &chain{*New<Chain>{terminator}(n).release()};
|
||||
chain.next.reset(&chain);
|
||||
|
@ -34,6 +34,12 @@ public:
|
||||
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) {
|
||||
CriticalSection critical{lock_};
|
||||
return Create(nextNewUnit_--, terminator);
|
||||
@ -72,6 +78,7 @@ private:
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
ExternalFileUnit *Find(const char *path);
|
||||
|
||||
ExternalFileUnit &Create(int, const Terminator &);
|
||||
|
||||
|
@ -59,20 +59,19 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
|
||||
ExternalFileUnit &result{
|
||||
GetUnitMap().LookUpOrCreate(unit, terminator, 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};
|
||||
result.OpenUnit(
|
||||
dir == Direction::Input ? OpenStatus::Old : OpenStatus::Replace,
|
||||
Action::ReadWrite, Position::Rewind, std::move(path),
|
||||
std::strlen(path.get()), Convert::Native, handler);
|
||||
result.OpenAnonymousUnit(
|
||||
dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
|
||||
Action::ReadWrite, Position::Rewind, Convert::Native, handler);
|
||||
result.isUnformatted = isUnformatted;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ExternalFileUnit *ExternalFileUnit::LookUp(const char *path) {
|
||||
return GetUnitMap().LookUp(path);
|
||||
}
|
||||
|
||||
ExternalFileUnit &ExternalFileUnit::CreateNew(
|
||||
int unit, const Terminator &terminator) {
|
||||
bool wasExtant{false};
|
||||
@ -125,10 +124,7 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
|
||||
handler.SignalError(IostatOpenBadRecl,
|
||||
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
|
||||
unitNumber(), static_cast<std::intmax_t>(*recordLength));
|
||||
} else if (!totalBytes) {
|
||||
handler.SignalError(IostatOpenUnknownSize,
|
||||
"OPEN(UNIT=%d,ACCESS='DIRECT'): file size is not known");
|
||||
} else if (*totalBytes % *recordLength != 0) {
|
||||
} else if (totalBytes && (*totalBytes % *recordLength != 0)) {
|
||||
handler.SignalError(IostatOpenBadAppend,
|
||||
"OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
|
||||
"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 (*totalBytes && recordLength && *recordLength) {
|
||||
if (totalBytes && recordLength && *recordLength) {
|
||||
endfileRecordNumber = 1 + (*totalBytes / *recordLength);
|
||||
} else {
|
||||
// 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) {
|
||||
DoImpliedEndfile(handler);
|
||||
Flush(handler);
|
||||
|
@ -35,6 +35,7 @@ class ExternalFileUnit : public ConnectionState,
|
||||
public:
|
||||
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {}
|
||||
int unitNumber() const { return unitNumber_; }
|
||||
bool swapEndianness() const { return swapEndianness_; }
|
||||
|
||||
static ExternalFileUnit *LookUp(int unit);
|
||||
static ExternalFileUnit &LookUpOrCrash(int unit, const Terminator &);
|
||||
@ -42,6 +43,7 @@ public:
|
||||
int unit, const Terminator &, bool &wasExtant);
|
||||
static ExternalFileUnit &LookUpOrCreateAnonymous(
|
||||
int unit, Direction, bool isUnformatted, const Terminator &);
|
||||
static ExternalFileUnit *LookUp(const char *path);
|
||||
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
|
||||
static ExternalFileUnit *LookUpForClose(int unit);
|
||||
static int NewUnit(const Terminator &);
|
||||
@ -51,13 +53,15 @@ public:
|
||||
void OpenUnit(OpenStatus, std::optional<Action>, Position,
|
||||
OwningPtr<char> &&path, std::size_t pathLength, Convert,
|
||||
IoErrorHandler &);
|
||||
void OpenAnonymousUnit(
|
||||
OpenStatus, std::optional<Action>, Position, Convert, IoErrorHandler &);
|
||||
void CloseUnit(CloseStatus, IoErrorHandler &);
|
||||
void DestroyClosed();
|
||||
|
||||
bool SetDirection(Direction, IoErrorHandler &);
|
||||
|
||||
template <typename A, typename... X>
|
||||
IoStatementState &BeginIoStatement(X &&... xs) {
|
||||
IoStatementState &BeginIoStatement(X &&...xs) {
|
||||
// TODO: Child data transfer statements vs. locking
|
||||
lock_.Take(); // dropped in EndIoStatement()
|
||||
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
|
||||
@ -111,7 +115,7 @@ private:
|
||||
ExternalListIoStatementState<Direction::Output>,
|
||||
ExternalListIoStatementState<Direction::Input>,
|
||||
UnformattedIoStatementState<Direction::Output>,
|
||||
UnformattedIoStatementState<Direction::Input>,
|
||||
UnformattedIoStatementState<Direction::Input>, InquireUnitState,
|
||||
ExternalMiscIoStatementState>
|
||||
u_;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user