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 
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         }
40       }
41     } else if (msg) {
42       va_list ap;
43       va_start(ap, msg);
44       CrashArgs(msg, ap);
45     } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
46       Crash(errstr);
47     } else {
48       Crash("I/O error (errno=%d): %s", iostatOrErrno,
49           std::strerror(iostatOrErrno));
50     }
51   }
52 }
53 
54 void IoErrorHandler::SignalError(int iostatOrErrno) {
55   SignalError(iostatOrErrno, nullptr);
56 }
57 
58 void IoErrorHandler::SignalErrno() { SignalError(errno); }
59 
60 void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
61 
62 void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
63 
64 bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
65   const char *msg{ioMsg_.get()};
66   if (!msg) {
67     msg = IostatErrorString(ioStat_);
68   }
69   if (msg) {
70     ToFortranDefaultCharacter(buffer, bufferLength, msg);
71     return true;
72   }
73 
74   char *newBuf;
75   // Following code is taken from llvm/lib/Support/Errno.cpp
76   // in LLVM v9.0.1
77 #if HAVE_STRERROR_R
78   // strerror_r is thread-safe.
79 #if defined(__GLIBC__) && defined(_GNU_SOURCE)
80   // glibc defines its own incompatible version of strerror_r
81   // which may not use the buffer supplied.
82   newBuf = ::strerror_r(ioStat_, buffer, bufferLength);
83 #else
84   return ::strerror_r(ioStat_, buffer, bufferLength) == 0;
85 #endif
86 #elif HAVE_DECL_STRERROR_S // "Windows Secure API"
87   return ::strerror_s(buffer, bufferLength, ioStat_) == 0;
88 #elif HAVE_STRERROR
89   // Copy the thread un-safe result of strerror into
90   // the buffer as fast as possible to minimize impact
91   // of collision of strerror in multiple threads.
92   newBuf = strerror(ioStat_);
93 #else
94   // Strange that this system doesn't even have strerror
95   return false;
96 #endif
97   ::strncpy(buffer, newBuf, bufferLength - 1);
98   buffer[bufferLength - 1] = '\n';
99   return true;
100 }
101 } // namespace Fortran::runtime::io
102