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