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
SignalError(int iostatOrErrno,const char * msg,...)20 void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
21 if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) {
22 if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
23 ioStat_ = IostatEnd;
24 }
25 } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) {
26 if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
27 ioStat_ = IostatEor; // least priority
28 }
29 } else if (iostatOrErrno != IostatOk) {
30 if (flags_ & (hasIoStat | hasErr)) {
31 if (ioStat_ <= 0) {
32 ioStat_ = iostatOrErrno; // priority over END=/EOR=
33 if (msg && (flags_ & hasIoMsg)) {
34 char buffer[256];
35 va_list ap;
36 va_start(ap, msg);
37 std::vsnprintf(buffer, sizeof buffer, msg, ap);
38 ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this);
39 va_end(ap);
40 }
41 }
42 } else if (msg) {
43 va_list ap;
44 va_start(ap, msg);
45 CrashArgs(msg, ap);
46 va_end(ap);
47 } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
48 Crash(errstr);
49 } else {
50 Crash("I/O error (errno=%d): %s", iostatOrErrno,
51 std::strerror(iostatOrErrno));
52 }
53 }
54 }
55
SignalError(int iostatOrErrno)56 void IoErrorHandler::SignalError(int iostatOrErrno) {
57 SignalError(iostatOrErrno, nullptr);
58 }
59
Forward(int ioStatOrErrno,const char * msg,std::size_t length)60 void IoErrorHandler::Forward(
61 int ioStatOrErrno, const char *msg, std::size_t length) {
62 SignalError(ioStatOrErrno);
63 if (ioStat_ != IostatOk && (flags_ & hasIoMsg)) {
64 ioMsg_ = SaveDefaultCharacter(msg, length, *this);
65 }
66 }
67
SignalErrno()68 void IoErrorHandler::SignalErrno() { SignalError(errno); }
69
SignalEnd()70 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
71
SignalEor()72 void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
73
GetIoMsg(char * buffer,std::size_t bufferLength)74 bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
75 const char *msg{ioMsg_.get()};
76 if (!msg) {
77 msg = IostatErrorString(ioStat_);
78 }
79 if (msg) {
80 ToFortranDefaultCharacter(buffer, bufferLength, msg);
81 return true;
82 }
83
84 char *newBuf;
85 // Following code is taken from llvm/lib/Support/Errno.cpp
86 // in LLVM v9.0.1
87 #if HAVE_STRERROR_R
88 // strerror_r is thread-safe.
89 #if defined(__GLIBC__) && defined(_GNU_SOURCE)
90 // glibc defines its own incompatible version of strerror_r
91 // which may not use the buffer supplied.
92 newBuf = ::strerror_r(ioStat_, buffer, bufferLength);
93 #else
94 return ::strerror_r(ioStat_, buffer, bufferLength) == 0;
95 #endif
96 #elif HAVE_DECL_STRERROR_S // "Windows Secure API"
97 return ::strerror_s(buffer, bufferLength, ioStat_) == 0;
98 #elif HAVE_STRERROR
99 // Copy the thread un-safe result of strerror into
100 // the buffer as fast as possible to minimize impact
101 // of collision of strerror in multiple threads.
102 newBuf = strerror(ioStat_);
103 #else
104 // Strange that this system doesn't even have strerror
105 return false;
106 #endif
107 ::strncpy(buffer, newBuf, bufferLength - 1);
108 buffer[bufferLength - 1] = '\n';
109 return true;
110 }
111 } // namespace Fortran::runtime::io
112