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