166 lines · cpp
1//===-- lib/runtime/io-error.cpp --------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang-rt/runtime/io-error.h"10#include "config.h"11#include "flang-rt/runtime/tools.h"12#include "flang/Runtime/magic-numbers.h"13#include <cerrno>14#include <cstdarg>15#include <cstdio>16#include <cstring>17 18namespace Fortran::runtime::io {19RT_OFFLOAD_API_GROUP_BEGIN20 21void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {22 // Note that IOMSG= alone without IOSTAT=/END=/EOR=/ERR= does not suffice23 // for error recovery (see F'2018 subclause 12.11).24 switch (iostatOrErrno) {25 case IostatOk:26 return;27 case IostatEnd:28 if ((flags_ & (hasIoStat | hasEnd)) ||29 ((flags_ & hasErr) && (flags_ & hasRec))) {30 // EOF goes to ERR= when REC= is present31 if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {32 ioStat_ = IostatEnd;33 }34 return;35 }36 break;37 case IostatEor:38 if (flags_ & (hasIoStat | hasEor)) {39 if (ioStat_ == IostatOk || ioStat_ < IostatEor) {40 ioStat_ = IostatEor; // least priority41 }42 return;43 }44 break;45 default:46 if (flags_ & (hasIoStat | hasErr)) {47 if (ioStat_ <= 0) {48 ioStat_ = iostatOrErrno; // priority over END=/EOR=49 if (msg && (flags_ & hasIoMsg)) {50#if !defined(RT_DEVICE_COMPILATION)51 char buffer[256];52 va_list ap;53 va_start(ap, msg);54 std::vsnprintf(buffer, sizeof buffer, msg, ap);55 va_end(ap);56#else57 const char *buffer = "not implemented yet: IOSTAT with varargs";58#endif59 ioMsg_ = SaveDefaultCharacter(60 buffer, Fortran::runtime::strlen(buffer) + 1, *this);61 }62 }63 return;64 }65 break;66 }67 // I/O error not caught!68 if (msg) {69#if !defined(RT_DEVICE_COMPILATION)70 va_list ap;71 va_start(ap, msg);72 CrashArgs(msg, ap);73 va_end(ap);74#else75 Crash("not implemented yet: IOSTAT with varargs");76#endif77 } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {78 Crash(errstr);79 } else {80#if !defined(RT_DEVICE_COMPILATION)81 Crash("I/O error (errno=%d): %s", iostatOrErrno,82 std::strerror(iostatOrErrno));83#else84 Crash("I/O error (errno=%d)", iostatOrErrno);85#endif86 }87}88 89void IoErrorHandler::SignalError(int iostatOrErrno) {90 SignalError(iostatOrErrno, nullptr);91}92 93void IoErrorHandler::Forward(94 int ioStatOrErrno, const char *msg, std::size_t length) {95 if (ioStatOrErrno != IostatOk) {96 if (msg) {97 SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);98 } else {99 SignalError(ioStatOrErrno);100 }101 }102}103 104void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }105 106void IoErrorHandler::SignalEor() { SignalError(IostatEor); }107 108void IoErrorHandler::SignalPendingError() {109 int error{pendingError_};110 pendingError_ = IostatOk;111 SignalError(error);112}113 114void IoErrorHandler::SignalErrno() { SignalError(errno); }115 116bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {117 const char *msg{ioMsg_.get()};118 if (!msg) {119 msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);120 }121 if (msg) {122 ToFortranDefaultCharacter(buffer, bufferLength, msg);123 return true;124 }125 126 // Following code is taken from llvm/lib/Support/Errno.cpp127 // in LLVM v9.0.1 with inadequate modification for Fortran,128 // since rectified.129 bool ok{false};130#if defined(RT_DEVICE_COMPILATION)131 // strerror_r is not available on device.132 msg = "errno description is not available on device";133#elif HAVE_STRERROR_R134 // strerror_r is thread-safe.135#if defined(__GLIBC__) && defined(_GNU_SOURCE)136 // glibc defines its own incompatible version of strerror_r137 // which may not use the buffer supplied.138 msg = ::strerror_r(ioStat_, buffer, bufferLength);139#else140 ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;141#endif142#elif HAVE_DECL_STRERROR_S // "Windows Secure API"143 ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;144#else145 // Copy the thread un-safe result of strerror into146 // the buffer as fast as possible to minimize impact147 // of collision of strerror in multiple threads.148 msg = strerror(ioStat_);149#endif150 if (msg) {151 ToFortranDefaultCharacter(buffer, bufferLength, msg);152 return true;153 } else if (ok) {154 std::size_t copied{Fortran::runtime::strlen(buffer)};155 if (copied < bufferLength) {156 runtime::memset(buffer + copied, ' ', bufferLength - copied);157 }158 return true;159 } else {160 return false;161 }162}163 164RT_OFFLOAD_API_GROUP_END165} // namespace Fortran::runtime::io166