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
18namespace Fortran::runtime::io {
19
20void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
21 // Note that IOMSG= alone without IOSTAT=/END=/EOR=/ERR= does not suffice
22 // for error recovery (see F'2018 subclause 12.11).
23 switch (iostatOrErrno) {
24 case IostatOk:
25 return;
26 case IostatEnd:
27 if (flags_ & (hasIoStat | hasEnd)) {
28 if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
29 ioStat_ = IostatEnd;
30 }
31 return;
32 }
33 break;
34 case IostatEor:
35 if (flags_ & (hasIoStat | hasEor)) {
36 if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
37 ioStat_ = IostatEor; // least priority
38 }
39 return;
40 }
41 break;
42 default:
43 if (flags_ & (hasIoStat | hasErr)) {
44 if (ioStat_ <= 0) {
45 ioStat_ = iostatOrErrno; // priority over END=/EOR=
46 if (msg && (flags_ & hasIoMsg)) {
47 char buffer[256];
48 va_list ap;
49 va_start(ap, msg);
50 std::vsnprintf(s: buffer, maxlen: sizeof buffer, format: msg, arg: ap);
51 ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this);
52 va_end(ap);
53 }
54 }
55 return;
56 }
57 break;
58 }
59 // I/O error not caught!
60 if (msg) {
61 va_list ap;
62 va_start(ap, msg);
63 CrashArgs(msg, ap);
64 va_end(ap);
65 } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
66 Crash(errstr);
67 } else {
68 Crash("I/O error (errno=%d): %s", iostatOrErrno,
69 std::strerror(errnum: iostatOrErrno));
70 }
71}
72
73void IoErrorHandler::SignalError(int iostatOrErrno) {
74 SignalError(iostatOrErrno, msg: nullptr);
75}
76
77void IoErrorHandler::Forward(
78 int ioStatOrErrno, const char *msg, std::size_t length) {
79 if (ioStatOrErrno != IostatOk) {
80 if (msg) {
81 SignalError(iostatOrErrno: ioStatOrErrno, msg: "%.*s", static_cast<int>(length), msg);
82 } else {
83 SignalError(iostatOrErrno: ioStatOrErrno);
84 }
85 }
86}
87
88void IoErrorHandler::SignalErrno() { SignalError(errno); }
89
90void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); }
91
92void IoErrorHandler::SignalEor() { SignalError(IostatEor); }
93
94void IoErrorHandler::SignalPendingError() {
95 int error{pendingError_};
96 pendingError_ = IostatOk;
97 SignalError(iostatOrErrno: error);
98}
99
100bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) {
101 const char *msg{ioMsg_.get()};
102 if (!msg) {
103 msg = IostatErrorString(ioStat_ == IostatOk ? pendingError_ : ioStat_);
104 }
105 if (msg) {
106 ToFortranDefaultCharacter(to: buffer, toLength: bufferLength, from: msg);
107 return true;
108 }
109
110 // Following code is taken from llvm/lib/Support/Errno.cpp
111 // in LLVM v9.0.1 with inadequate modification for Fortran,
112 // since rectified.
113 bool ok{false};
114#if HAVE_STRERROR_R
115 // strerror_r is thread-safe.
116#if defined(__GLIBC__) && defined(_GNU_SOURCE)
117 // glibc defines its own incompatible version of strerror_r
118 // which may not use the buffer supplied.
119 msg = ::strerror_r(ioStat_, buffer, bufferLength);
120#else
121 ok = ::strerror_r(ioStat_, buffer, bufferLength) == 0;
122#endif
123#elif HAVE_DECL_STRERROR_S // "Windows Secure API"
124 ok = ::strerror_s(buffer, bufferLength, ioStat_) == 0;
125#else
126 // Copy the thread un-safe result of strerror into
127 // the buffer as fast as possible to minimize impact
128 // of collision of strerror in multiple threads.
129 msg = strerror(ioStat_);
130#endif
131 if (msg) {
132 ToFortranDefaultCharacter(to: buffer, toLength: bufferLength, from: msg);
133 return true;
134 } else if (ok) {
135 std::size_t copied{std::strlen(s: buffer)};
136 if (copied < bufferLength) {
137 std::memset(s: buffer + copied, c: ' ', n: bufferLength - copied);
138 }
139 return true;
140 } else {
141 return false;
142 }
143}
144} // namespace Fortran::runtime::io
145

source code of flang/runtime/io-error.cpp