236
|
1 //===-- runtime/io-error.cpp ----------------------------------------------===//
|
173
|
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"
|
236
|
12 #include "flang/Runtime/magic-numbers.h"
|
173
|
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, ...) {
|
252
|
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;
|
173
|
32 }
|
252
|
33 break;
|
|
34 case IostatEor:
|
|
35 if (flags_ & (hasIoStat | hasEor)) {
|
|
36 if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
|
|
37 ioStat_ = IostatEor; // least priority
|
|
38 }
|
|
39 return;
|
173
|
40 }
|
252
|
41 break;
|
|
42 default:
|
|
43 if (flags_ & (hasIoStat | hasErr)) {
|
173
|
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(buffer, sizeof buffer, msg, ap);
|
|
51 ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this);
|
221
|
52 va_end(ap);
|
173
|
53 }
|
|
54 }
|
252
|
55 return;
|
173
|
56 }
|
252
|
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(iostatOrErrno));
|
173
|
70 }
|
|
71 }
|
|
72
|
|
73 void IoErrorHandler::SignalError(int iostatOrErrno) {
|
|
74 SignalError(iostatOrErrno, nullptr);
|
|
75 }
|
|
76
|
223
|
77 void IoErrorHandler::Forward(
|
|
78 int ioStatOrErrno, const char *msg, std::size_t length) {
|
252
|
79 if (ioStatOrErrno != IostatOk) {
|
|
80 if (msg) {
|
|
81 SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
|
|
82 } else {
|
|
83 SignalError(ioStatOrErrno);
|
|
84 }
|
236
|
85 }
|
223
|
86 }
|
|
87
|
173
|
88 void IoErrorHandler::SignalErrno() { SignalError(errno); }
|
|
89
|
|
90 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
|
|
91
|
|
92 void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
|
|
93
|
236
|
94 void IoErrorHandler::SignalPendingError() {
|
|
95 int error{pendingError_};
|
|
96 pendingError_ = IostatOk;
|
|
97 SignalError(error);
|
|
98 }
|
|
99
|
173
|
100 bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
|
|
101 const char *msg{ioMsg_.get()};
|
|
102 if (!msg) {
|
236
|
103 msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);
|
173
|
104 }
|
|
105 if (msg) {
|
|
106 ToFortranDefaultCharacter(buffer, bufferLength, msg);
|
|
107 return true;
|
|
108 }
|
|
109
|
|
110 // Following code is taken from llvm/lib/Support/Errno.cpp
|
236
|
111 // in LLVM v9.0.1 with inadequate modification for Fortran,
|
|
112 // since rectified.
|
|
113 bool ok{false};
|
173
|
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.
|
236
|
119 msg = ::strerror_r(ioStat_, buffer, bufferLength);
|
173
|
120 #else
|
236
|
121 ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;
|
173
|
122 #endif
|
|
123 #elif HAVE_DECL_STRERROR_S // "Windows Secure API"
|
236
|
124 ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;
|
252
|
125 #else
|
173
|
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.
|
236
|
129 msg = strerror(ioStat_);
|
173
|
130 #endif
|
236
|
131 if (msg) {
|
|
132 ToFortranDefaultCharacter(buffer, bufferLength, msg);
|
|
133 return true;
|
|
134 } else if (ok) {
|
|
135 std::size_t copied{std::strlen(buffer)};
|
|
136 if (copied < bufferLength) {
|
|
137 std::memset(buffer + copied, ' ', bufferLength - copied);
|
|
138 }
|
|
139 return true;
|
|
140 } else {
|
|
141 return false;
|
|
142 }
|
173
|
143 }
|
|
144 } // namespace Fortran::runtime::io
|