xref: /llvm-project/flang/runtime/io-error.cpp (revision bf95854e9ab1209901603d8f9edba4328eed6689)
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         ((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 
89 void IoErrorHandler::SignalError(int iostatOrErrno) {
90   SignalError(iostatOrErrno, nullptr);
91 }
92 
93 void 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 
104 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
105 
106 void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
107 
108 void IoErrorHandler::SignalPendingError() {
109   int error{pendingError_};
110   pendingError_ = IostatOk;
111   SignalError(error);
112 }
113 
114 void IoErrorHandler::SignalErrno() { SignalError(errno); }
115 
116 bool 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 
164 RT_OFFLOAD_API_GROUP_END
165 } // namespace Fortran::runtime::io
166