diff --git a/format-test.cc b/format-test.cc index 52535aec..fa5d8ab6 100644 --- a/format-test.cc +++ b/format-test.cc @@ -248,6 +248,22 @@ TEST(UtilTest, UTF8ToUTF16) { // TODO: test UTF16ToUTF8::Convert #endif // _WIN32 +TEST(UtilTest, StrError) { + using fmt::internal::StrError; + EXPECT_DEBUG_DEATH(StrError(EDOM, 0, 0), "Assertion"); + char buffer[BUFFER_SIZE]; + EXPECT_DEBUG_DEATH(StrError(EDOM, buffer, 0), "Assertion"); + buffer[0] = 'x'; + const char *message = StrError(-1, buffer, 1); + EXPECT_EQ(ERANGE, errno); + EXPECT_STREQ("", message); + message = StrError(-1, buffer, BUFFER_SIZE); + EXPECT_EQ(0, errno); + EXPECT_GE(BUFFER_SIZE - 1, std::strlen(message)); + message = StrError(-1, buffer, std::strlen(message)); + EXPECT_EQ(ERANGE, errno); +} + class TestString { private: std::string value_; diff --git a/format.cc b/format.cc index 19779696..d4846f2c 100644 --- a/format.cc +++ b/format.cc @@ -204,6 +204,26 @@ int fmt::internal::UTF16ToUTF8::Convert(fmt::WStringRef s) { #endif +char *fmt::internal::StrError( + int error_code, char *buffer, std::size_t buffer_size) { + assert(buffer != 0 && buffer_size != 0); + errno = 0; +#ifdef _GNU_SOURCE + char *message = strerror_r(error_code, buffer, buffer_size); + if (message == buffer && strlen(buffer) == buffer_size - 1) + errno = ERANGE; // The buffer is full so the message is probably truncated. + return message; +#elif _WIN32 + errno = strerror_s(buffer, buflen, error_code); + if (errno == 0 && std::strlen(buffer) == buffer_size - 1) + errno = ERANGE; // The buffer is full so the message is probably truncated. + return buffer; +#else + strerror_r(error_code, buffer, buffer_size); + return buffer; +#endif +} + void fmt::internal::FormatSystemErrorMessage( fmt::Writer &out, int error_code, fmt::StringRef message) { #ifndef _WIN32 diff --git a/format.h b/format.h index 82130656..d56a5a27 100644 --- a/format.h +++ b/format.h @@ -489,6 +489,16 @@ class UTF16ToUTF8 { }; #endif +// Portable thread-safe version of strerror. +// Returns a pointer to a string describing the error code. This can be +// either a pointer to a string stored in buffer, or a pointer to some +// static immutable string. Sets errno to one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +char *StrError(int error_code, char *buffer, std::size_t buffer_size); + // Formats a system error message writing the output to out. void FormatSystemErrorMessage(Writer &out, int error_code, StringRef message); @@ -1521,7 +1531,6 @@ inline Formatter Format(WStringRef format) { return f; } - /** A sink that gets the system error message corresponding to the error code and throws SystemError.