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
18namespace Fortran::runtime::io {
19RT_OFFLOAD_API_GROUP_BEGIN
20
21void 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
87void IoErrorHandler::SignalError(int iostatOrErrno) {
88 SignalError(iostatOrErrno, nullptr);
89}
90
91void 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
102void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
103
104void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
105
106void IoErrorHandler::SignalPendingError() {
107 int error{pendingError_};
108 pendingError_ = IostatOk;
109 SignalError(error);
110}
111
112void IoErrorHandler::SignalErrno() { SignalError(errno); }
113
114bool 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
162RT_OFFLOAD_API_GROUP_END
163} // namespace Fortran::runtime::io
164

source code of flang/runtime/io-error.cpp