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 RT_OFFLOAD_API_GROUP_BEGIN 20 21 void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) { 22 // Note that IOMSG= alone without IOSTAT=/END=/EOR=/ERR= does not suffice 23 // 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 present 31 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 priority 41 } 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 #else 57 const char *buffer = "not implemented yet: IOSTAT with varargs"; 58 #endif 59 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 #else 75 Crash("not implemented yet: IOSTAT with varargs"); 76 #endif 77 } 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 #else 84 Crash("I/O error (errno=%d)", iostatOrErrno); 85 #endif 86 } 87 } 88 89 void IoErrorHandler::SignalError(int iostatOrErrno) { 90 SignalError(iostatOrErrno, nullptr); 91 } 92 93 void 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 104 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } 105 106 void IoErrorHandler::SignalEor() { SignalError(IostatEor); } 107 108 void IoErrorHandler::SignalPendingError() { 109 int error{pendingError_}; 110 pendingError_ = IostatOk; 111 SignalError(error); 112 } 113 114 void IoErrorHandler::SignalErrno() { SignalError(errno); } 115 116 bool 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.cpp 127 // 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_R 134 // strerror_r is thread-safe. 135 #if defined(__GLIBC__) && defined(_GNU_SOURCE) 136 // glibc defines its own incompatible version of strerror_r 137 // which may not use the buffer supplied. 138 msg = ::strerror_r(ioStat_, buffer, bufferLength); 139 #else 140 ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0; 141 #endif 142 #elif HAVE_DECL_STRERROR_S // "Windows Secure API" 143 ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0; 144 #else 145 // Copy the thread un-safe result of strerror into 146 // the buffer as fast as possible to minimize impact 147 // of collision of strerror in multiple threads. 148 msg = strerror(ioStat_); 149 #endif 150 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 std::memset(buffer + copied, ' ', bufferLength - copied); 157 } 158 return true; 159 } else { 160 return false; 161 } 162 } 163 164 RT_OFFLOAD_API_GROUP_END 165 } // namespace Fortran::runtime::io 166