1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include <qglobal.h> |
5 | #include "qsystemerror_p.h" |
6 | #include <errno.h> |
7 | #if defined(Q_CC_MSVC) |
8 | # include <crtdbg.h> |
9 | #endif |
10 | #ifdef Q_OS_WIN |
11 | # include <qt_windows.h> |
12 | # include <comdef.h> |
13 | #endif |
14 | #ifndef QT_BOOTSTRAPPED |
15 | # include <qcoreapplication.h> |
16 | #endif |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | using namespace Qt::StringLiterals; |
21 | |
22 | #if !defined(Q_OS_WIN) && QT_CONFIG(thread) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) && \ |
23 | defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L |
24 | namespace { |
25 | // There are two incompatible versions of strerror_r: |
26 | // a) the XSI/POSIX.1 version, which returns an int, |
27 | // indicating success or not |
28 | // b) the GNU version, which returns a char*, which may or may not |
29 | // be the beginning of the buffer we used |
30 | // The GNU libc manpage for strerror_r says you should use the XSI |
31 | // version in portable code. However, it's impossible to do that if |
32 | // _GNU_SOURCE is defined so we use C++ overloading to decide what to do |
33 | // depending on the return type |
34 | [[maybe_unused]] static inline QString fromstrerror_helper(int, const QByteArray &buf) |
35 | { |
36 | return QString::fromLocal8Bit(buf); |
37 | } |
38 | [[maybe_unused]] static inline QString fromstrerror_helper(const char *str, const QByteArray &) |
39 | { |
40 | return QString::fromLocal8Bit(str); |
41 | } |
42 | } |
43 | #endif |
44 | |
45 | #ifdef Q_OS_WIN |
46 | static QString windowsErrorString(int errorCode) |
47 | { |
48 | QString ret; |
49 | wchar_t *string = nullptr; |
50 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
51 | NULL, |
52 | errorCode, |
53 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
54 | (LPWSTR)&string, |
55 | 0, |
56 | NULL); |
57 | ret = QString::fromWCharArray(string); |
58 | LocalFree((HLOCAL)string); |
59 | |
60 | if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND) |
61 | ret = QString::fromLatin1("The specified module could not be found." ); |
62 | if (ret.endsWith("\r\n"_L1 )) |
63 | ret.chop(2); |
64 | if (ret.isEmpty()) |
65 | ret = QString::fromLatin1("Unknown error 0x%1." ) |
66 | .arg(unsigned(errorCode), 8, 16, '0'_L1); |
67 | return ret; |
68 | } |
69 | #endif |
70 | |
71 | static QString standardLibraryErrorString(int errorCode) |
72 | { |
73 | const char *s = nullptr; |
74 | QString ret; |
75 | switch (errorCode) { |
76 | case 0: |
77 | break; |
78 | case EACCES: |
79 | s = QT_TRANSLATE_NOOP("QIODevice" , "Permission denied" ); |
80 | break; |
81 | case EMFILE: |
82 | s = QT_TRANSLATE_NOOP("QIODevice" , "Too many open files" ); |
83 | break; |
84 | case ENOENT: |
85 | s = QT_TRANSLATE_NOOP("QIODevice" , "No such file or directory" ); |
86 | break; |
87 | case ENOSPC: |
88 | s = QT_TRANSLATE_NOOP("QIODevice" , "No space left on device" ); |
89 | break; |
90 | default: { |
91 | #if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) |
92 | QByteArray buf(1024, Qt::Uninitialized); |
93 | ret = fromstrerror_helper(strerror_r(errorCode, buf.data(), buf.size()), buf); |
94 | #else |
95 | ret = QString::fromLocal8Bit(ba: strerror(errnum: errorCode)); |
96 | #endif |
97 | break; } |
98 | } |
99 | if (s) { |
100 | #ifndef QT_BOOTSTRAPPED |
101 | ret = QCoreApplication::translate(context: "QIODevice" , key: s); |
102 | #else |
103 | ret = QString::fromLatin1(s); |
104 | #endif |
105 | } |
106 | return ret.trimmed(); |
107 | } |
108 | |
109 | QString QSystemError::string(ErrorScope errorScope, int errorCode) |
110 | { |
111 | switch (errorScope) { |
112 | case NativeError: |
113 | #if defined(Q_OS_WIN) |
114 | return windowsErrorString(errorCode); |
115 | #endif // else unix: native and standard library are the same |
116 | case StandardLibraryError: |
117 | return standardLibraryErrorString(errorCode); |
118 | default: |
119 | qWarning(msg: "invalid error scope" ); |
120 | Q_FALLTHROUGH(); |
121 | case NoError: |
122 | return u"No error"_s ; |
123 | } |
124 | } |
125 | |
126 | QString QSystemError::stdString(int errorCode) |
127 | { |
128 | return standardLibraryErrorString(errorCode: errorCode == -1 ? errno : errorCode); |
129 | } |
130 | |
131 | #ifdef Q_OS_WIN |
132 | QString QSystemError::windowsString(int errorCode) |
133 | { |
134 | return windowsErrorString(errorCode == -1 ? GetLastError() : errorCode); |
135 | } |
136 | |
137 | QString QSystemError::windowsComString(HRESULT hr) |
138 | { |
139 | const _com_error comError(hr); |
140 | QString result = "COM error 0x"_L1 + QString::number(ulong(hr), 16); |
141 | if (const wchar_t *msg = comError.ErrorMessage()) |
142 | result += ": "_L1 + QString::fromWCharArray(msg); |
143 | return result; |
144 | } |
145 | |
146 | QString qt_error_string(int code) |
147 | { |
148 | return windowsErrorString(code == -1 ? GetLastError() : code); |
149 | } |
150 | #else |
151 | QString qt_error_string(int code) |
152 | { |
153 | return standardLibraryErrorString(errorCode: code == -1 ? errno : code); |
154 | } |
155 | #endif |
156 | |
157 | QT_END_NAMESPACE |
158 | |