1 //===-- 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "io-error.h" 10 #include "config.h" 11 #include "magic-numbers.h" 12 #include "tools.h" 13 #include <cerrno> 14 #include <cstdarg> 15 #include <cstdio> 16 #include <cstring> 17 18 namespace Fortran::runtime::io { 19 20 void IoErrorHandler::Begin(const char *sourceFileName, int sourceLine) { 21 flags_ = 0; 22 ioStat_ = 0; 23 ioMsg_.reset(); 24 SetLocation(sourceFileName, sourceLine); 25 } 26 27 void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) { 28 if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) { 29 if (!ioStat_ || ioStat_ < IostatEnd) { 30 ioStat_ = IostatEnd; 31 } 32 } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) { 33 if (!ioStat_ || ioStat_ < IostatEor) { 34 ioStat_ = IostatEor; // least priority 35 } 36 } else if (iostatOrErrno != IostatOk) { 37 if (flags_ & (hasIoStat | hasErr)) { 38 if (ioStat_ <= 0) { 39 ioStat_ = iostatOrErrno; // priority over END=/EOR= 40 if (msg && (flags_ & hasIoMsg)) { 41 char buffer[256]; 42 va_list ap; 43 va_start(ap, msg); 44 std::vsnprintf(buffer, sizeof buffer, msg, ap); 45 ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this); 46 } 47 } 48 } else if (msg) { 49 va_list ap; 50 va_start(ap, msg); 51 CrashArgs(msg, ap); 52 } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) { 53 Crash(errstr); 54 } else { 55 Crash("I/O error (errno=%d): %s", iostatOrErrno, 56 std::strerror(iostatOrErrno)); 57 } 58 } 59 } 60 61 void IoErrorHandler::SignalError(int iostatOrErrno) { 62 SignalError(iostatOrErrno, nullptr); 63 } 64 65 void IoErrorHandler::SignalErrno() { SignalError(errno); } 66 67 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } 68 69 void IoErrorHandler::SignalEor() { SignalError(IostatEor); } 70 71 bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) { 72 const char *msg{ioMsg_.get()}; 73 if (!msg) { 74 msg = IostatErrorString(ioStat_); 75 } 76 if (msg) { 77 ToFortranDefaultCharacter(buffer, bufferLength, msg); 78 return true; 79 } 80 81 char *newBuf; 82 // Following code is taken from llvm/lib/Support/Errno.cpp 83 // in LLVM v9.0.1 84 #if HAVE_STRERROR_R 85 // strerror_r is thread-safe. 86 #if defined(__GLIBC__) && defined(_GNU_SOURCE) 87 // glibc defines its own incompatible version of strerror_r 88 // which may not use the buffer supplied. 89 newBuf = ::strerror_r(ioStat_, buffer, bufferLength); 90 #else 91 return ::strerror_r(ioStat_, buffer, bufferLength) == 0; 92 #endif 93 #elif HAVE_DECL_STRERROR_S // "Windows Secure API" 94 return ::strerror_s(buffer, bufferLength, ioStat_) == 0; 95 #elif HAVE_STRERROR 96 // Copy the thread un-safe result of strerror into 97 // the buffer as fast as possible to minimize impact 98 // of collision of strerror in multiple threads. 99 newBuf = strerror(ioStat_); 100 #else 101 // Strange that this system doesn't even have strerror 102 return false; 103 #endif 104 ::strncpy(buffer, newBuf, bufferLength - 1); 105 buffer[bufferLength - 1] = '\n'; 106 return true; 107 } 108 } // namespace Fortran::runtime::io 109