xref: /llvm-project/flang/runtime/io-error.cpp (revision bf95854e9ab1209901603d8f9edba4328eed6689)
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 {
198ebf7411SSlava Zakharin RT_OFFLOAD_API_GROUP_BEGIN
20352d347aSAlexis Perry 
213b635714Speter klausler void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
220a8a572cSPeter Klausler   // Note that IOMSG= alone without IOSTAT=/END=/EOR=/ERR= does not suffice
230a8a572cSPeter Klausler   // for error recovery (see F'2018 subclause 12.11).
240a8a572cSPeter Klausler   switch (iostatOrErrno) {
250a8a572cSPeter Klausler   case IostatOk:
260a8a572cSPeter Klausler     return;
270a8a572cSPeter Klausler   case IostatEnd:
28*bf95854eSPeter Klausler     if ((flags_ & (hasIoStat | hasEnd)) ||
29*bf95854eSPeter Klausler         ((flags_ & hasErr) && (flags_ & hasRec))) {
30*bf95854eSPeter Klausler       // EOF goes to ERR= when REC= is present
31e81c96d6Speter klausler       if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
323b635714Speter klausler         ioStat_ = IostatEnd;
333b635714Speter klausler       }
340a8a572cSPeter Klausler       return;
350a8a572cSPeter Klausler     }
360a8a572cSPeter Klausler     break;
370a8a572cSPeter Klausler   case IostatEor:
380a8a572cSPeter Klausler     if (flags_ & (hasIoStat | hasEor)) {
39e51939ecSpeter klausler       if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
403b635714Speter klausler         ioStat_ = IostatEor; // least priority
413b635714Speter klausler       }
420a8a572cSPeter Klausler       return;
430a8a572cSPeter Klausler     }
440a8a572cSPeter Klausler     break;
450a8a572cSPeter Klausler   default:
460a8a572cSPeter Klausler     if (flags_ & (hasIoStat | hasErr)) {
47352d347aSAlexis Perry       if (ioStat_ <= 0) {
48352d347aSAlexis Perry         ioStat_ = iostatOrErrno; // priority over END=/EOR=
493b635714Speter klausler         if (msg && (flags_ & hasIoMsg)) {
508ebf7411SSlava Zakharin #if !defined(RT_DEVICE_COMPILATION)
513b635714Speter klausler           char buffer[256];
523b635714Speter klausler           va_list ap;
533b635714Speter klausler           va_start(ap, msg);
543b635714Speter klausler           std::vsnprintf(buffer, sizeof buffer, msg, ap);
554f41994cSpeter klausler           va_end(ap);
568ebf7411SSlava Zakharin #else
578ebf7411SSlava Zakharin           const char *buffer = "not implemented yet: IOSTAT with varargs";
588ebf7411SSlava Zakharin #endif
598ebf7411SSlava Zakharin           ioMsg_ = SaveDefaultCharacter(
608ebf7411SSlava Zakharin               buffer, Fortran::runtime::strlen(buffer) + 1, *this);
61352d347aSAlexis Perry         }
623b635714Speter klausler       }
630a8a572cSPeter Klausler       return;
640a8a572cSPeter Klausler     }
650a8a572cSPeter Klausler     break;
660a8a572cSPeter Klausler   }
670a8a572cSPeter Klausler   // I/O error not caught!
680a8a572cSPeter Klausler   if (msg) {
698ebf7411SSlava Zakharin #if !defined(RT_DEVICE_COMPILATION)
703b635714Speter klausler     va_list ap;
713b635714Speter klausler     va_start(ap, msg);
723b635714Speter klausler     CrashArgs(msg, ap);
734f41994cSpeter klausler     va_end(ap);
748ebf7411SSlava Zakharin #else
758ebf7411SSlava Zakharin     Crash("not implemented yet: IOSTAT with varargs");
768ebf7411SSlava Zakharin #endif
773b635714Speter klausler   } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
783b635714Speter klausler     Crash(errstr);
79352d347aSAlexis Perry   } else {
808ebf7411SSlava Zakharin #if !defined(RT_DEVICE_COMPILATION)
813b635714Speter klausler     Crash("I/O error (errno=%d): %s", iostatOrErrno,
823b635714Speter klausler         std::strerror(iostatOrErrno));
838ebf7411SSlava Zakharin #else
848ebf7411SSlava Zakharin     Crash("I/O error (errno=%d)", iostatOrErrno);
858ebf7411SSlava Zakharin #endif
86352d347aSAlexis Perry   }
87352d347aSAlexis Perry }
88352d347aSAlexis Perry 
893b635714Speter klausler void IoErrorHandler::SignalError(int iostatOrErrno) {
903b635714Speter klausler   SignalError(iostatOrErrno, nullptr);
913b635714Speter klausler }
923b635714Speter klausler 
9343fadefbSpeter klausler void IoErrorHandler::Forward(
9443fadefbSpeter klausler     int ioStatOrErrno, const char *msg, std::size_t length) {
9579f6b812SPeter Klausler   if (ioStatOrErrno != IostatOk) {
9679f6b812SPeter Klausler     if (msg) {
97e9d0f8baSPeter Klausler       SignalError(ioStatOrErrno, "%.*s", static_cast<int>(length), msg);
98e9d0f8baSPeter Klausler     } else {
99e9d0f8baSPeter Klausler       SignalError(ioStatOrErrno);
100e9d0f8baSPeter Klausler     }
10143fadefbSpeter klausler   }
10279f6b812SPeter Klausler }
10343fadefbSpeter klausler 
1043b635714Speter klausler void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
105352d347aSAlexis Perry 
1063b635714Speter klausler void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
1073b635714Speter klausler 
1088db4dc86SPeter Klausler void IoErrorHandler::SignalPendingError() {
1098db4dc86SPeter Klausler   int error{pendingError_};
1108db4dc86SPeter Klausler   pendingError_ = IostatOk;
1118db4dc86SPeter Klausler   SignalError(error);
1128db4dc86SPeter Klausler }
1138db4dc86SPeter Klausler 
1148ebf7411SSlava Zakharin void IoErrorHandler::SignalErrno() { SignalError(errno); }
1158ebf7411SSlava Zakharin 
1163b635714Speter klausler bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
1173b635714Speter klausler   const char *msg{ioMsg_.get()};
1183b635714Speter klausler   if (!msg) {
1198db4dc86SPeter Klausler     msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);
120352d347aSAlexis Perry   }
1213b635714Speter klausler   if (msg) {
1223b635714Speter klausler     ToFortranDefaultCharacter(buffer, bufferLength, msg);
1233b635714Speter klausler     return true;
124352d347aSAlexis Perry   }
125ea5efd1eSIsuru Fernando 
126ea5efd1eSIsuru Fernando   // Following code is taken from llvm/lib/Support/Errno.cpp
1278db4dc86SPeter Klausler   // in LLVM v9.0.1 with inadequate modification for Fortran,
1288db4dc86SPeter Klausler   // since rectified.
1298db4dc86SPeter Klausler   bool ok{false};
130f3c31d70SSlava Zakharin #if defined(RT_DEVICE_COMPILATION)
131f3c31d70SSlava Zakharin   // strerror_r is not available on device.
132f3c31d70SSlava Zakharin   msg = "errno description is not available on device";
133f3c31d70SSlava Zakharin #elif HAVE_STRERROR_R
134ea5efd1eSIsuru Fernando   // strerror_r is thread-safe.
135ea5efd1eSIsuru Fernando #if defined(__GLIBC__) && defined(_GNU_SOURCE)
136ea5efd1eSIsuru Fernando   // glibc defines its own incompatible version of strerror_r
137ea5efd1eSIsuru Fernando   // which may not use the buffer supplied.
1388db4dc86SPeter Klausler   msg = ::strerror_r(ioStat_, buffer, bufferLength);
139ea5efd1eSIsuru Fernando #else
1408db4dc86SPeter Klausler   ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;
141ea5efd1eSIsuru Fernando #endif
142ea5efd1eSIsuru Fernando #elif HAVE_DECL_STRERROR_S // "Windows Secure API"
1438db4dc86SPeter Klausler   ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;
14443bec337SFangrui Song #else
145ea5efd1eSIsuru Fernando   // Copy the thread un-safe result of strerror into
146ea5efd1eSIsuru Fernando   // the buffer as fast as possible to minimize impact
147ea5efd1eSIsuru Fernando   // of collision of strerror in multiple threads.
1488db4dc86SPeter Klausler   msg = strerror(ioStat_);
149ea5efd1eSIsuru Fernando #endif
1508db4dc86SPeter Klausler   if (msg) {
1518db4dc86SPeter Klausler     ToFortranDefaultCharacter(buffer, bufferLength, msg);
152ea5efd1eSIsuru Fernando     return true;
1538db4dc86SPeter Klausler   } else if (ok) {
1548ebf7411SSlava Zakharin     std::size_t copied{Fortran::runtime::strlen(buffer)};
1558db4dc86SPeter Klausler     if (copied < bufferLength) {
1568db4dc86SPeter Klausler       std::memset(buffer + copied, ' ', bufferLength - copied);
1578db4dc86SPeter Klausler     }
1588db4dc86SPeter Klausler     return true;
1598db4dc86SPeter Klausler   } else {
1608db4dc86SPeter Klausler     return false;
1618db4dc86SPeter Klausler   }
162352d347aSAlexis Perry }
163f3c31d70SSlava Zakharin 
164f3c31d70SSlava Zakharin RT_OFFLOAD_API_GROUP_END
1651f879005STim Keith } // namespace Fortran::runtime::io
166