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 | hasIoMsg | 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   if (ioStat_ != IostatOk && msg && (flags_ & hasIoMsg)) {
63     ioMsg_ = SaveDefaultCharacter(msg, length, *this);
64   }
65   if (ioStatOrErrno != IostatOk && msg) {
66     SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
67   } else {
68     SignalError(ioStatOrErrno);
69   }
70 }
71 
SignalErrno()72 void IoErrorHandler::SignalErrno() { SignalError(errno); }
73 
SignalEnd()74 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
75 
SignalEor()76 void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
77 
SignalPendingError()78 void IoErrorHandler::SignalPendingError() {
79   int error{pendingError_};
80   pendingError_ = IostatOk;
81   SignalError(error);
82 }
83 
GetIoMsg(char * buffer,std::size_t bufferLength)84 bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
85   const char *msg{ioMsg_.get()};
86   if (!msg) {
87     msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);
88   }
89   if (msg) {
90     ToFortranDefaultCharacter(buffer, bufferLength, msg);
91     return true;
92   }
93 
94   // Following code is taken from llvm/lib/Support/Errno.cpp
95   // in LLVM v9.0.1 with inadequate modification for Fortran,
96   // since rectified.
97   bool ok{false};
98 #if HAVE_STRERROR_R
99   // strerror_r is thread-safe.
100 #if defined(__GLIBC__) && defined(_GNU_SOURCE)
101   // glibc defines its own incompatible version of strerror_r
102   // which may not use the buffer supplied.
103   msg = ::strerror_r(ioStat_, buffer, bufferLength);
104 #else
105   ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;
106 #endif
107 #elif HAVE_DECL_STRERROR_S // "Windows Secure API"
108   ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;
109 #elif HAVE_STRERROR
110   // Copy the thread un-safe result of strerror into
111   // the buffer as fast as possible to minimize impact
112   // of collision of strerror in multiple threads.
113   msg = strerror(ioStat_);
114 #else
115   // Strange that this system doesn't even have strerror
116   return false;
117 #endif
118   if (msg) {
119     ToFortranDefaultCharacter(buffer, bufferLength, msg);
120     return true;
121   } else if (ok) {
122     std::size_t copied{std::strlen(buffer)};
123     if (copied < bufferLength) {
124       std::memset(buffer + copied, ' ', bufferLength - copied);
125     }
126     return true;
127   } else {
128     return false;
129   }
130 }
131 } // namespace Fortran::runtime::io
132