Fix issue #2274.
This commit is contained in:
parent
2a9b314627
commit
0036a1d195
@ -136,6 +136,8 @@ template <typename Char> struct formatter<std::error_code, Char> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// A converter from UTF-16 to UTF-8.
|
// A converter from UTF-16 to UTF-8.
|
||||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||||
@ -202,6 +204,10 @@ std::system_error windows_error(int error_code, string_view message,
|
|||||||
// Can be used to report errors from destructors.
|
// Can be used to report errors from destructors.
|
||||||
FMT_API void report_windows_error(int error_code,
|
FMT_API void report_windows_error(int error_code,
|
||||||
const char* message) FMT_NOEXCEPT;
|
const char* message) FMT_NOEXCEPT;
|
||||||
|
#else
|
||||||
|
inline const std::error_category& system_category() FMT_NOEXCEPT {
|
||||||
|
return std::system_category();
|
||||||
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
// std::system is not available on some platforms such as iOS (#2248).
|
// std::system is not available on some platforms such as iOS (#2248).
|
||||||
|
79
src/os.cc
79
src/os.cc
@ -100,35 +100,78 @@ int detail::utf16_to_utf8::convert(wstring_view s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class system_message {
|
||||||
|
system_message(const system_message&) = delete;
|
||||||
|
void operator=(const system_message&) = delete;
|
||||||
|
|
||||||
|
unsigned long result_;
|
||||||
|
wchar_t* message_;
|
||||||
|
|
||||||
|
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT {
|
||||||
|
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit system_message(unsigned long error_code)
|
||||||
|
: result_(0), message_(nullptr) {
|
||||||
|
result_ = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
|
||||||
|
if (result_ != 0) {
|
||||||
|
while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
|
||||||
|
--result_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~system_message() { LocalFree(message_); }
|
||||||
|
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
|
||||||
|
operator wstring_view() const FMT_NOEXCEPT {
|
||||||
|
return wstring_view(message_, result_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class utf8_system_category final : public std::error_category {
|
||||||
|
public:
|
||||||
|
const char* name() const FMT_NOEXCEPT override { return "system"; }
|
||||||
|
std::string message(int error_code) const override {
|
||||||
|
system_message msg(error_code);
|
||||||
|
if (msg) {
|
||||||
|
utf16_to_utf8 utf8_message;
|
||||||
|
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||||
|
return utf8_message.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown error";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_API const std::error_category& system_category() FMT_NOEXCEPT {
|
||||||
|
static const detail::utf8_system_category category;
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
std::system_error vwindows_error(int err_code, string_view format_str,
|
std::system_error vwindows_error(int err_code, string_view format_str,
|
||||||
format_args args) {
|
format_args args) {
|
||||||
auto ec = std::error_code(err_code, std::system_category());
|
auto ec = std::error_code(err_code, system_category());
|
||||||
throw std::system_error(ec, vformat(format_str, args));
|
throw std::system_error(ec, vformat(format_str, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
||||||
const char* message) FMT_NOEXCEPT {
|
const char* message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
wmemory_buffer buf;
|
system_message msg(error_code);
|
||||||
buf.resize(inline_buffer_size);
|
if (msg) {
|
||||||
for (;;) {
|
|
||||||
wchar_t* system_message = &buf[0];
|
|
||||||
int result = FormatMessageW(
|
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
|
||||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
|
|
||||||
static_cast<uint32_t>(buf.size()), nullptr);
|
|
||||||
if (result != 0) {
|
|
||||||
utf16_to_utf8 utf8_message;
|
utf16_to_utf8 utf8_message;
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||||
format_to(buffer_appender<char>(out), "{}: {}", message,
|
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||||
utf8_message);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
break; // Can't get error message, report error code instead.
|
|
||||||
buf.resize(buf.size() * 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FMT_CATCH(...) {}
|
FMT_CATCH(...) {}
|
||||||
|
@ -45,7 +45,6 @@ void check_utf_conversion_error(
|
|||||||
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
|
||||||
fmt::memory_buffer out;
|
fmt::memory_buffer out;
|
||||||
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
||||||
out.resize(out.size() - 2); // Remove newline.
|
|
||||||
auto error = std::system_error(std::error_code());
|
auto error = std::system_error(std::error_code());
|
||||||
try {
|
try {
|
||||||
(Converter)(str);
|
(Converter)(str);
|
||||||
@ -74,10 +73,10 @@ TEST(os_test, format_std_error_code) {
|
|||||||
std::error_code(42, std::generic_category())));
|
std::error_code(42, std::generic_category())));
|
||||||
EXPECT_EQ("system:42",
|
EXPECT_EQ("system:42",
|
||||||
fmt::format(FMT_STRING("{0}"),
|
fmt::format(FMT_STRING("{0}"),
|
||||||
std::error_code(42, std::system_category())));
|
std::error_code(42, fmt::system_category())));
|
||||||
EXPECT_EQ("system:-42",
|
EXPECT_EQ("system:-42",
|
||||||
fmt::format(FMT_STRING("{0}"),
|
fmt::format(FMT_STRING("{0}"),
|
||||||
std::error_code(-42, std::system_category())));
|
std::error_code(-42, fmt::system_category())));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(os_test, format_std_error_code_wide) {
|
TEST(os_test, format_std_error_code_wide) {
|
||||||
@ -86,10 +85,10 @@ TEST(os_test, format_std_error_code_wide) {
|
|||||||
std::error_code(42, std::generic_category())));
|
std::error_code(42, std::generic_category())));
|
||||||
EXPECT_EQ(L"system:42",
|
EXPECT_EQ(L"system:42",
|
||||||
fmt::format(FMT_STRING(L"{0}"),
|
fmt::format(FMT_STRING(L"{0}"),
|
||||||
std::error_code(42, std::system_category())));
|
std::error_code(42, fmt::system_category())));
|
||||||
EXPECT_EQ(L"system:-42",
|
EXPECT_EQ(L"system:-42",
|
||||||
fmt::format(FMT_STRING(L"{0}"),
|
fmt::format(FMT_STRING(L"{0}"),
|
||||||
std::error_code(-42, std::system_category())));
|
std::error_code(-42, fmt::system_category())));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(os_test, format_windows_error) {
|
TEST(os_test, format_windows_error) {
|
||||||
@ -99,7 +98,8 @@ TEST(os_test, format_windows_error) {
|
|||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
||||||
fmt::detail::utf16_to_utf8 utf8_message(message);
|
fmt::detail::utf16_to_utf8 utf8_message(
|
||||||
|
fmt::wstring_view(message, result - 2));
|
||||||
LocalFree(message);
|
LocalFree(message);
|
||||||
fmt::memory_buffer actual_message;
|
fmt::memory_buffer actual_message;
|
||||||
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
|
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
|
||||||
@ -120,8 +120,12 @@ TEST(os_test, format_long_windows_error) {
|
|||||||
0, static_cast<DWORD>(provisioning_not_allowed),
|
0, static_cast<DWORD>(provisioning_not_allowed),
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
reinterpret_cast<LPWSTR>(&message), 0, 0);
|
||||||
EXPECT_NE(result, 0);
|
if (result == 0) {
|
||||||
fmt::detail::utf16_to_utf8 utf8_message(message);
|
LocalFree(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fmt::detail::utf16_to_utf8 utf8_message(
|
||||||
|
fmt::wstring_view(message, result - 2));
|
||||||
LocalFree(message);
|
LocalFree(message);
|
||||||
fmt::memory_buffer actual_message;
|
fmt::memory_buffer actual_message;
|
||||||
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
|
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
|
||||||
@ -139,7 +143,6 @@ TEST(os_test, windows_error) {
|
|||||||
}
|
}
|
||||||
fmt::memory_buffer message;
|
fmt::memory_buffer message;
|
||||||
fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
|
fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
|
||||||
message.resize(message.size() - 2);
|
|
||||||
EXPECT_THAT(error.what(), HasSubstr(to_string(message)));
|
EXPECT_THAT(error.what(), HasSubstr(to_string(message)));
|
||||||
EXPECT_EQ(ERROR_FILE_EXISTS, error.code().value());
|
EXPECT_EQ(ERROR_FILE_EXISTS, error.code().value());
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ TEST(file_test, size) {
|
|||||||
}
|
}
|
||||||
fstat_sim = none;
|
fstat_sim = none;
|
||||||
EXPECT_EQ(error_code,
|
EXPECT_EQ(error_code,
|
||||||
std::error_code(ERROR_ACCESS_DENIED, std::system_category()));
|
std::error_code(ERROR_ACCESS_DENIED, fmt::system_category()));
|
||||||
# else
|
# else
|
||||||
f.close();
|
f.close();
|
||||||
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
|
EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes");
|
||||||
|
Loading…
Reference in New Issue
Block a user