1 //===-- runtime/io-error.cpp ----------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "io-error.h" 10 #include "config.h" 11 #include "tools.h" 12 #include "flang/Runtime/magic-numbers.h" 13 #include <cerrno> 14 #include <cstdarg> 15 #include <cstdio> 16 #include <cstring> 17 18 namespace Fortran::runtime::io { 19 20 void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) { 21 if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) { 22 if (ioStat_ == IostatOk || ioStat_ < IostatEnd) { 23 ioStat_ = IostatEnd; 24 } 25 } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) { 26 if (ioStat_ == IostatOk || ioStat_ < IostatEor) { 27 ioStat_ = IostatEor; // least priority 28 } 29 } else if (iostatOrErrno != IostatOk) { 30 if (flags_ & (hasIoStat | hasIoMsg | hasErr)) { 31 if (ioStat_ <= 0) { 32 ioStat_ = iostatOrErrno; // priority over END=/EOR= 33 if (msg && (flags_ & hasIoMsg)) { 34 char buffer[256]; 35 va_list ap; 36 va_start(ap, msg); 37 std::vsnprintf(buffer, sizeof buffer, msg, ap); 38 ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this); 39 va_end(ap); 40 } 41 } 42 } else if (msg) { 43 va_list ap; 44 va_start(ap, msg); 45 CrashArgs(msg, ap); 46 va_end(ap); 47 } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) { 48 Crash(errstr); 49 } else { 50 Crash("I/O error (errno=%d): %s", iostatOrErrno, 51 std::strerror(iostatOrErrno)); 52 } 53 } 54 } 55 56 void IoErrorHandler::SignalError(int iostatOrErrno) { 57 SignalError(iostatOrErrno, nullptr); 58 } 59 60 void IoErrorHandler::Forward( 61 int ioStatOrErrno, const char *msg, std::size_t length) { 62 if (ioStat_ != IostatOk && msg && (flags_ & hasIoMsg)) { 63 ioMsg_ = SaveDefaultCharacter(msg, length, *this); 64 } 65 if (ioStatOrErrno != IostatOk && msg) { 66 SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg); 67 } else { 68 SignalError(ioStatOrErrno); 69 } 70 } 71 72 void IoErrorHandler::SignalErrno() { SignalError(errno); } 73 74 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } 75 76 void IoErrorHandler::SignalEor() { SignalError(IostatEor); } 77 78 void IoErrorHandler::SignalPendingError() { 79 int error{pendingError_}; 80 pendingError_ = IostatOk; 81 SignalError(error); 82 } 83 84 bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) { 85 const char *msg{ioMsg_.get()}; 86 if (!msg) { 87 msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_); 88 } 89 if (msg) { 90 ToFortranDefaultCharacter(buffer, bufferLength, msg); 91 return true; 92 } 93 94 // Following code is taken from llvm/lib/Support/Errno.cpp 95 // in LLVM v9.0.1 with inadequate modification for Fortran, 96 // since rectified. 97 bool ok{false}; 98 #if HAVE_STRERROR_R 99 // strerror_r is thread-safe. 100 #if defined(__GLIBC__) && defined(_GNU_SOURCE) 101 // glibc defines its own incompatible version of strerror_r 102 // which may not use the buffer supplied. 103 msg = ::strerror_r(ioStat_, buffer, bufferLength); 104 #else 105 ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0; 106 #endif 107 #elif HAVE_DECL_STRERROR_S // "Windows Secure API" 108 ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0; 109 #elif HAVE_STRERROR 110 // Copy the thread un-safe result of strerror into 111 // the buffer as fast as possible to minimize impact 112 // of collision of strerror in multiple threads. 113 msg = strerror(ioStat_); 114 #else 115 // Strange that this system doesn't even have strerror 116 return false; 117 #endif 118 if (msg) { 119 ToFortranDefaultCharacter(buffer, bufferLength, msg); 120 return true; 121 } else if (ok) { 122 std::size_t copied{std::strlen(buffer)}; 123 if (copied < bufferLength) { 124 std::memset(buffer + copied, ' ', bufferLength - copied); 125 } 126 return true; 127 } else { 128 return false; 129 } 130 } 131 } // namespace Fortran::runtime::io 132