1651f58bfSDiana Picus //===-- runtime/io-error.cpp ----------------------------------------------===//
2352d347aSAlexis Perry //
3352d347aSAlexis Perry // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4352d347aSAlexis Perry // See https://llvm.org/LICENSE.txt for license information.
5352d347aSAlexis Perry // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6352d347aSAlexis Perry //
7352d347aSAlexis Perry //===----------------------------------------------------------------------===//
8352d347aSAlexis Perry 
9352d347aSAlexis Perry #include "io-error.h"
101f879005STim Keith #include "config.h"
113b635714Speter klausler #include "tools.h"
12830c0b90SPeter Klausler #include "flang/Runtime/magic-numbers.h"
13352d347aSAlexis Perry #include <cerrno>
143b635714Speter klausler #include <cstdarg>
15352d347aSAlexis Perry #include <cstdio>
16352d347aSAlexis Perry #include <cstring>
17352d347aSAlexis Perry 
18352d347aSAlexis Perry namespace Fortran::runtime::io {
19352d347aSAlexis Perry 
SignalError(int iostatOrErrno,const char * msg,...)203b635714Speter klausler void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
213b635714Speter klausler   if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) {
22e81c96d6Speter klausler     if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
233b635714Speter klausler       ioStat_ = IostatEnd;
243b635714Speter klausler     }
253b635714Speter klausler   } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) {
26e51939ecSpeter klausler     if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
273b635714Speter klausler       ioStat_ = IostatEor; // least priority
283b635714Speter klausler     }
293b635714Speter klausler   } else if (iostatOrErrno != IostatOk) {
30*8db4dc86SPeter Klausler     if (flags_ & (hasIoStat | hasIoMsg | hasErr)) {
31352d347aSAlexis Perry       if (ioStat_ <= 0) {
32352d347aSAlexis Perry         ioStat_ = iostatOrErrno; // priority over END=/EOR=
333b635714Speter klausler         if (msg && (flags_ & hasIoMsg)) {
343b635714Speter klausler           char buffer[256];
353b635714Speter klausler           va_list ap;
363b635714Speter klausler           va_start(ap, msg);
373b635714Speter klausler           std::vsnprintf(buffer, sizeof buffer, msg, ap);
383b635714Speter klausler           ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this);
394f41994cSpeter klausler           va_end(ap);
40352d347aSAlexis Perry         }
413b635714Speter klausler       }
423b635714Speter klausler     } else if (msg) {
433b635714Speter klausler       va_list ap;
443b635714Speter klausler       va_start(ap, msg);
453b635714Speter klausler       CrashArgs(msg, ap);
464f41994cSpeter klausler       va_end(ap);
473b635714Speter klausler     } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
483b635714Speter klausler       Crash(errstr);
49352d347aSAlexis Perry     } else {
503b635714Speter klausler       Crash("I/O error (errno=%d): %s", iostatOrErrno,
513b635714Speter klausler           std::strerror(iostatOrErrno));
52352d347aSAlexis Perry     }
53352d347aSAlexis Perry   }
54352d347aSAlexis Perry }
55352d347aSAlexis Perry 
SignalError(int iostatOrErrno)563b635714Speter klausler void IoErrorHandler::SignalError(int iostatOrErrno) {
573b635714Speter klausler   SignalError(iostatOrErrno, nullptr);
583b635714Speter klausler }
593b635714Speter klausler 
Forward(int ioStatOrErrno,const char * msg,std::size_t length)6043fadefbSpeter klausler void IoErrorHandler::Forward(
6143fadefbSpeter klausler     int ioStatOrErrno, const char *msg, std::size_t length) {
62e9d0f8baSPeter Klausler   if (ioStat_ != IostatOk && msg && (flags_ & hasIoMsg)) {
6343fadefbSpeter klausler     ioMsg_ = SaveDefaultCharacter(msg, length, *this);
6443fadefbSpeter klausler   }
65e9d0f8baSPeter Klausler   if (ioStatOrErrno != IostatOk && msg) {
66e9d0f8baSPeter Klausler     SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
67e9d0f8baSPeter Klausler   } else {
68e9d0f8baSPeter Klausler     SignalError(ioStatOrErrno);
69e9d0f8baSPeter Klausler   }
7043fadefbSpeter klausler }
7143fadefbSpeter klausler 
SignalErrno()72352d347aSAlexis Perry void IoErrorHandler::SignalErrno() { SignalError(errno); }
73352d347aSAlexis Perry 
SignalEnd()743b635714Speter klausler void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
75352d347aSAlexis Perry 
SignalEor()763b635714Speter klausler void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
773b635714Speter klausler 
SignalPendingError()78*8db4dc86SPeter Klausler void IoErrorHandler::SignalPendingError() {
79*8db4dc86SPeter Klausler   int error{pendingError_};
80*8db4dc86SPeter Klausler   pendingError_ = IostatOk;
81*8db4dc86SPeter Klausler   SignalError(error);
82*8db4dc86SPeter Klausler }
83*8db4dc86SPeter Klausler 
GetIoMsg(char * buffer,std::size_t bufferLength)843b635714Speter klausler bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
853b635714Speter klausler   const char *msg{ioMsg_.get()};
863b635714Speter klausler   if (!msg) {
87*8db4dc86SPeter Klausler     msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);
88352d347aSAlexis Perry   }
893b635714Speter klausler   if (msg) {
903b635714Speter klausler     ToFortranDefaultCharacter(buffer, bufferLength, msg);
913b635714Speter klausler     return true;
92352d347aSAlexis Perry   }
93ea5efd1eSIsuru Fernando 
94ea5efd1eSIsuru Fernando   // Following code is taken from llvm/lib/Support/Errno.cpp
95*8db4dc86SPeter Klausler   // in LLVM v9.0.1 with inadequate modification for Fortran,
96*8db4dc86SPeter Klausler   // since rectified.
97*8db4dc86SPeter Klausler   bool ok{false};
98ea5efd1eSIsuru Fernando #if HAVE_STRERROR_R
99ea5efd1eSIsuru Fernando   // strerror_r is thread-safe.
100ea5efd1eSIsuru Fernando #if defined(__GLIBC__) && defined(_GNU_SOURCE)
101ea5efd1eSIsuru Fernando   // glibc defines its own incompatible version of strerror_r
102ea5efd1eSIsuru Fernando   // which may not use the buffer supplied.
103*8db4dc86SPeter Klausler   msg = ::strerror_r(ioStat_, buffer, bufferLength);
104ea5efd1eSIsuru Fernando #else
105*8db4dc86SPeter Klausler   ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;
106ea5efd1eSIsuru Fernando #endif
107ea5efd1eSIsuru Fernando #elif HAVE_DECL_STRERROR_S // "Windows Secure API"
108*8db4dc86SPeter Klausler   ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;
109ea5efd1eSIsuru Fernando #elif HAVE_STRERROR
110ea5efd1eSIsuru Fernando   // Copy the thread un-safe result of strerror into
111ea5efd1eSIsuru Fernando   // the buffer as fast as possible to minimize impact
112ea5efd1eSIsuru Fernando   // of collision of strerror in multiple threads.
113*8db4dc86SPeter Klausler   msg = strerror(ioStat_);
114ea5efd1eSIsuru Fernando #else
115ea5efd1eSIsuru Fernando   // Strange that this system doesn't even have strerror
116ea5efd1eSIsuru Fernando   return false;
117ea5efd1eSIsuru Fernando #endif
118*8db4dc86SPeter Klausler   if (msg) {
119*8db4dc86SPeter Klausler     ToFortranDefaultCharacter(buffer, bufferLength, msg);
120ea5efd1eSIsuru Fernando     return true;
121*8db4dc86SPeter Klausler   } else if (ok) {
122*8db4dc86SPeter Klausler     std::size_t copied{std::strlen(buffer)};
123*8db4dc86SPeter Klausler     if (copied < bufferLength) {
124*8db4dc86SPeter Klausler       std::memset(buffer + copied, ' ', bufferLength - copied);
125*8db4dc86SPeter Klausler     }
126*8db4dc86SPeter Klausler     return true;
127*8db4dc86SPeter Klausler   } else {
128*8db4dc86SPeter Klausler     return false;
129*8db4dc86SPeter Klausler   }
130352d347aSAlexis Perry }
1311f879005STim Keith } // namespace Fortran::runtime::io
132