1//===-- lib/runtime/io-error.cpp --------------------------------*- C++ -*-===//
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 "flang-rt/runtime/io-error.h"
10#include "config.h"
11#include "flang-rt/runtime/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 ((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
89void IoErrorHandler::SignalError(int iostatOrErrno) {
90 SignalError(iostatOrErrno, nullptr);
91}
92
93void 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
104void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
105
106void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
107
108void IoErrorHandler::SignalPendingError() {
109 int error{pendingError_};
110 pendingError_ = IostatOk;
111 SignalError(error);
112}
113
114void IoErrorHandler::SignalErrno() { SignalError(errno); }
115
116bool 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
164RT_OFFLOAD_API_GROUP_END
165} // namespace Fortran::runtime::io
166

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