[libc++][Android] Allow testing libc++ with clang-r536225 (#116149)
[llvm-project.git] / flang / runtime / io-error.cpp
blob7a90966f81047f361181a67e7b35089b51bc701d
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 //===----------------------------------------------------------------------===//
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>
18 namespace Fortran::runtime::io {
19 RT_OFFLOAD_API_GROUP_BEGIN
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;
32 return;
34 break;
35 case IostatEor:
36 if (flags_ & (hasIoStat | hasEor)) {
37 if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
38 ioStat_ = IostatEor; // least priority
40 return;
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(buffer, sizeof buffer, msg, 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);
61 return;
63 break;
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(iostatOrErrno));
81 #else
82 Crash("I/O error (errno=%d)", iostatOrErrno);
83 #endif
87 void IoErrorHandler::SignalError(int iostatOrErrno) {
88 SignalError(iostatOrErrno, nullptr);
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);
102 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
104 void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
106 void IoErrorHandler::SignalPendingError() {
107 int error{pendingError_};
108 pendingError_ = IostatOk;
109 SignalError(error);
112 void IoErrorHandler::SignalErrno() { SignalError(errno); }
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_);
119 if (msg) {
120 ToFortranDefaultCharacter(buffer, bufferLength, msg);
121 return true;
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(buffer, bufferLength, msg);
150 return true;
151 } else if (ok) {
152 std::size_t copied{Fortran::runtime::strlen(buffer)};
153 if (copied < bufferLength) {
154 std::memset(buffer + copied, ' ', bufferLength - copied);
156 return true;
157 } else {
158 return false;
162 RT_OFFLOAD_API_GROUP_END
163 } // namespace Fortran::runtime::io