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 | if (ioStat_ == IostatOk || ioStat_ < IostatEnd) { |
30 | ioStat_ = IostatEnd; |
31 | } |
32 | return; |
33 | } |
34 | break; |
35 | case IostatEor: |
36 | if (flags_ & (hasIoStat | hasEor)) { |
37 | if (ioStat_ == IostatOk || ioStat_ < IostatEor) { |
38 | ioStat_ = IostatEor; // least priority |
39 | } |
40 | return; |
41 | } |
42 | break; |
43 | default: |
44 | if (flags_ & (hasIoStat | hasErr)) { |
45 | if (ioStat_ <= 0) { |
46 | ioStat_ = iostatOrErrno; // priority over END=/EOR= |
47 | if (msg && (flags_ & hasIoMsg)) { |
48 | #if !defined(RT_DEVICE_COMPILATION) |
49 | char buffer[256]; |
50 | va_list ap; |
51 | va_start(ap, msg); |
52 | std::vsnprintf(s: buffer, maxlen: sizeof buffer, format: msg, arg: ap); |
53 | va_end(ap); |
54 | #else |
55 | const char *buffer = "not implemented yet: IOSTAT with varargs" ; |
56 | #endif |
57 | ioMsg_ = SaveDefaultCharacter( |
58 | buffer, Fortran::runtime::strlen(buffer) + 1, *this); |
59 | } |
60 | } |
61 | return; |
62 | } |
63 | break; |
64 | } |
65 | // I/O error not caught! |
66 | if (msg) { |
67 | #if !defined(RT_DEVICE_COMPILATION) |
68 | va_list ap; |
69 | va_start(ap, msg); |
70 | CrashArgs(msg, ap); |
71 | va_end(ap); |
72 | #else |
73 | Crash("not implemented yet: IOSTAT with varargs" ); |
74 | #endif |
75 | } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) { |
76 | Crash(errstr); |
77 | } else { |
78 | #if !defined(RT_DEVICE_COMPILATION) |
79 | Crash("I/O error (errno=%d): %s" , iostatOrErrno, |
80 | std::strerror(errnum: iostatOrErrno)); |
81 | #else |
82 | Crash("I/O error (errno=%d)" , iostatOrErrno); |
83 | #endif |
84 | } |
85 | } |
86 | |
87 | void IoErrorHandler::SignalError(int iostatOrErrno) { |
88 | SignalError(iostatOrErrno, nullptr); |
89 | } |
90 | |
91 | void IoErrorHandler::Forward( |
92 | int ioStatOrErrno, const char *msg, std::size_t length) { |
93 | if (ioStatOrErrno != IostatOk) { |
94 | if (msg) { |
95 | SignalError(ioStatOrErrno, "%.*s" , static_cast<int>(length), msg); |
96 | } else { |
97 | SignalError(ioStatOrErrno); |
98 | } |
99 | } |
100 | } |
101 | |
102 | void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } |
103 | |
104 | void IoErrorHandler::SignalEor() { SignalError(IostatEor); } |
105 | |
106 | void IoErrorHandler::SignalPendingError() { |
107 | int error{pendingError_}; |
108 | pendingError_ = IostatOk; |
109 | SignalError(error); |
110 | } |
111 | |
112 | void IoErrorHandler::SignalErrno() { SignalError(errno); } |
113 | |
114 | bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) { |
115 | const char *msg{ioMsg_.get()}; |
116 | if (!msg) { |
117 | msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_); |
118 | } |
119 | if (msg) { |
120 | ToFortranDefaultCharacter(to: buffer, toLength: bufferLength, from: msg); |
121 | return true; |
122 | } |
123 | |
124 | // Following code is taken from llvm/lib/Support/Errno.cpp |
125 | // in LLVM v9.0.1 with inadequate modification for Fortran, |
126 | // since rectified. |
127 | bool ok{false}; |
128 | #if defined(RT_DEVICE_COMPILATION) |
129 | // strerror_r is not available on device. |
130 | msg = "errno description is not available on device" ; |
131 | #elif HAVE_STRERROR_R |
132 | // strerror_r is thread-safe. |
133 | #if defined(__GLIBC__) && defined(_GNU_SOURCE) |
134 | // glibc defines its own incompatible version of strerror_r |
135 | // which may not use the buffer supplied. |
136 | msg = ::strerror_r(ioStat_, buffer, bufferLength); |
137 | #else |
138 | ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0; |
139 | #endif |
140 | #elif HAVE_DECL_STRERROR_S // "Windows Secure API" |
141 | ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0; |
142 | #else |
143 | // Copy the thread un-safe result of strerror into |
144 | // the buffer as fast as possible to minimize impact |
145 | // of collision of strerror in multiple threads. |
146 | msg = strerror(ioStat_); |
147 | #endif |
148 | if (msg) { |
149 | ToFortranDefaultCharacter(to: buffer, toLength: bufferLength, from: msg); |
150 | return true; |
151 | } else if (ok) { |
152 | std::size_t copied{Fortran::runtime::strlen(buffer)}; |
153 | if (copied < bufferLength) { |
154 | std::memset(s: buffer + copied, c: ' ', n: bufferLength - copied); |
155 | } |
156 | return true; |
157 | } else { |
158 | return false; |
159 | } |
160 | } |
161 | |
162 | RT_OFFLOAD_API_GROUP_END |
163 | } // namespace Fortran::runtime::io |
164 | |