forked from OSchip/llvm-project
132 lines
4.0 KiB
C++
132 lines
4.0 KiB
C++
//===-- runtime/io-error.cpp ----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "io-error.h"
|
|
#include "config.h"
|
|
#include "tools.h"
|
|
#include "flang/Runtime/magic-numbers.h"
|
|
#include <cerrno>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
namespace Fortran::runtime::io {
|
|
|
|
void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
|
|
if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) {
|
|
if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
|
|
ioStat_ = IostatEnd;
|
|
}
|
|
} else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) {
|
|
if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
|
|
ioStat_ = IostatEor; // least priority
|
|
}
|
|
} else if (iostatOrErrno != IostatOk) {
|
|
if (flags_ & (hasIoStat | hasIoMsg | hasErr)) {
|
|
if (ioStat_ <= 0) {
|
|
ioStat_ = iostatOrErrno; // priority over END=/EOR=
|
|
if (msg && (flags_ & hasIoMsg)) {
|
|
char buffer[256];
|
|
va_list ap;
|
|
va_start(ap, msg);
|
|
std::vsnprintf(buffer, sizeof buffer, msg, ap);
|
|
ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
} else if (msg) {
|
|
va_list ap;
|
|
va_start(ap, msg);
|
|
CrashArgs(msg, ap);
|
|
va_end(ap);
|
|
} else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
|
|
Crash(errstr);
|
|
} else {
|
|
Crash("I/O error (errno=%d): %s", iostatOrErrno,
|
|
std::strerror(iostatOrErrno));
|
|
}
|
|
}
|
|
}
|
|
|
|
void IoErrorHandler::SignalError(int iostatOrErrno) {
|
|
SignalError(iostatOrErrno, nullptr);
|
|
}
|
|
|
|
void IoErrorHandler::Forward(
|
|
int ioStatOrErrno, const char *msg, std::size_t length) {
|
|
if (ioStat_ != IostatOk && msg && (flags_ & hasIoMsg)) {
|
|
ioMsg_ = SaveDefaultCharacter(msg, length, *this);
|
|
}
|
|
if (ioStatOrErrno != IostatOk && msg) {
|
|
SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
|
|
} else {
|
|
SignalError(ioStatOrErrno);
|
|
}
|
|
}
|
|
|
|
void IoErrorHandler::SignalErrno() { SignalError(errno); }
|
|
|
|
void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
|
|
|
|
void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
|
|
|
|
void IoErrorHandler::SignalPendingError() {
|
|
int error{pendingError_};
|
|
pendingError_ = IostatOk;
|
|
SignalError(error);
|
|
}
|
|
|
|
bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
|
|
const char *msg{ioMsg_.get()};
|
|
if (!msg) {
|
|
msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);
|
|
}
|
|
if (msg) {
|
|
ToFortranDefaultCharacter(buffer, bufferLength, msg);
|
|
return true;
|
|
}
|
|
|
|
// Following code is taken from llvm/lib/Support/Errno.cpp
|
|
// in LLVM v9.0.1 with inadequate modification for Fortran,
|
|
// since rectified.
|
|
bool ok{false};
|
|
#if HAVE_STRERROR_R
|
|
// strerror_r is thread-safe.
|
|
#if defined(__GLIBC__) && defined(_GNU_SOURCE)
|
|
// glibc defines its own incompatible version of strerror_r
|
|
// which may not use the buffer supplied.
|
|
msg = ::strerror_r(ioStat_, buffer, bufferLength);
|
|
#else
|
|
ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;
|
|
#endif
|
|
#elif HAVE_DECL_STRERROR_S // "Windows Secure API"
|
|
ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;
|
|
#elif HAVE_STRERROR
|
|
// Copy the thread un-safe result of strerror into
|
|
// the buffer as fast as possible to minimize impact
|
|
// of collision of strerror in multiple threads.
|
|
msg = strerror(ioStat_);
|
|
#else
|
|
// Strange that this system doesn't even have strerror
|
|
return false;
|
|
#endif
|
|
if (msg) {
|
|
ToFortranDefaultCharacter(buffer, bufferLength, msg);
|
|
return true;
|
|
} else if (ok) {
|
|
std::size_t copied{std::strlen(buffer)};
|
|
if (copied < bufferLength) {
|
|
std::memset(buffer + copied, ' ', bufferLength - copied);
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
} // namespace Fortran::runtime::io
|