diff --git a/format.cc b/format.cc index ac4535d8..f04c04b3 100644 --- a/format.cc +++ b/format.cc @@ -138,6 +138,43 @@ const char RESET_COLOR[] = "\x1b[0m"; typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); +// Portable thread-safe version of strerror. +// Sets buffer to point 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. +// Returns 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. +int safe_strerror( + int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true) { + assert(buffer != 0 && buffer_size != 0); + int result = 0; +#ifdef _GNU_SOURCE + char *message = strerror_r(error_code, buffer, buffer_size); + // If the buffer is full then the message is probably truncated. + if (message == buffer && strlen(buffer) == buffer_size - 1) + result = ERANGE; + buffer = message; +#elif __MINGW32__ + errno = 0; + (void)buffer_size; + buffer = strerror(error_code); + result = errno; +#elif _WIN32 + result = strerror_s(buffer, buffer_size, error_code); + // If the buffer is full then the message is probably truncated. + if (result == 0 && std::strlen(buffer) == buffer_size - 1) + result = ERANGE; +#else + result = strerror_r(error_code, buffer, buffer_size); + if (result == -1) + result = errno; // glibc versions before 2.13 return result in errno. +#endif + return result; +} + void format_error_code(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) { // Report error code making sure that the output fits into @@ -442,34 +479,6 @@ void fmt::WindowsError::init( #endif -int fmt::internal::safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true) { - assert(buffer != 0 && buffer_size != 0); - int result = 0; -#ifdef _GNU_SOURCE - char *message = strerror_r(error_code, buffer, buffer_size); - // If the buffer is full then the message is probably truncated. - if (message == buffer && strlen(buffer) == buffer_size - 1) - result = ERANGE; - buffer = message; -#elif __MINGW32__ - errno = 0; - (void)buffer_size; - buffer = strerror(error_code); - result = errno; -#elif _WIN32 - result = strerror_s(buffer, buffer_size, error_code); - // If the buffer is full then the message is probably truncated. - if (result == 0 && std::strlen(buffer) == buffer_size - 1) - result = ERANGE; -#else - result = strerror_r(error_code, buffer, buffer_size); - if (result == -1) - result = errno; // glibc versions before 2.13 return result in errno. -#endif - return result; -} - void fmt::internal::format_system_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) { diff --git a/format.h b/format.h index ceb0d85a..956079f3 100644 --- a/format.h +++ b/format.h @@ -534,18 +534,6 @@ class UTF16ToUTF8 { }; #endif -// Portable thread-safe version of strerror. -// Sets buffer to point 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. -// Returns 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. -int safe_strerror(int error_code, - char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true); - void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT(true); diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 467c13cd..3967686e 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -27,7 +27,11 @@ // Include format.cc instead of format.h to test implementation-specific stuff. #include "format.cc" + +#include + #include "gtest-extra.h" +#include "util.h" #undef max @@ -48,6 +52,39 @@ TEST(FormatTest, FormatNegativeNaN) { fmt::print("Warning: compiler doesn't handle negative NaN correctly"); } +TEST(FormatTest, StrError) { + char *message = 0; + char buffer[BUFFER_SIZE]; +#ifndef NDEBUG + EXPECT_DEBUG_DEATH(safe_strerror(EDOM, message = 0, 0), "Assertion"); + EXPECT_DEBUG_DEATH(safe_strerror(EDOM, message = buffer, 0), "Assertion"); +#endif + buffer[0] = 'x'; +#ifdef _GNU_SOURCE + // Use invalid error code to make sure that safe_strerror returns an error + // message in the buffer rather than a pointer to a static string. + int error_code = -1; +#else + int error_code = EDOM; +#endif + + int result = safe_strerror(error_code, message = buffer, BUFFER_SIZE); + EXPECT_EQ(0, result); + std::size_t message_size = std::strlen(message); + EXPECT_GE(BUFFER_SIZE - 1u, message_size); + EXPECT_EQ(get_system_error(error_code), message); + + // safe_strerror never uses buffer on MinGW. +#ifndef __MINGW32__ + result = safe_strerror(error_code, message = buffer, message_size); + EXPECT_EQ(ERANGE, result); + result = safe_strerror(error_code, message = buffer, 1); + EXPECT_EQ(buffer, message); // Message should point to buffer. + EXPECT_EQ(ERANGE, result); + EXPECT_STREQ("", message); +#endif +} + TEST(FormatTest, FormatErrorCode) { std::string msg = "error 42", sep = ": "; { diff --git a/test/util-test.cc b/test/util-test.cc index be7cc2c7..d3872130 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -46,18 +46,6 @@ using fmt::internal::Arg; using fmt::internal::MakeArg; namespace { -std::string get_system_error(int error_code) { -#if defined(__MINGW32__) || !defined(_WIN32) - return strerror(error_code); -#else - enum { BUFFER_SIZE = 200 }; - char buffer[BUFFER_SIZE]; - EXPECT_EQ(0, strerror_s(buffer, BUFFER_SIZE, error_code)); - std::size_t max_len = BUFFER_SIZE - 1; - EXPECT_LT(std::strlen(buffer), max_len); - return buffer; -#endif -} struct Test {}; template @@ -417,40 +405,6 @@ TEST(UtilTest, UTF16ToUTF8Convert) { } #endif // _WIN32 -TEST(UtilTest, StrError) { - using fmt::internal::safe_strerror; - char *message = 0; - char buffer[BUFFER_SIZE]; -#ifndef NDEBUG - EXPECT_DEBUG_DEATH(safe_strerror(EDOM, message = 0, 0), "Assertion"); - EXPECT_DEBUG_DEATH(safe_strerror(EDOM, message = buffer, 0), "Assertion"); -#endif - buffer[0] = 'x'; -#ifdef _GNU_SOURCE - // Use invalid error code to make sure that safe_strerror returns an error - // message in the buffer rather than a pointer to a static string. - int error_code = -1; -#else - int error_code = EDOM; -#endif - - int result = safe_strerror(error_code, message = buffer, BUFFER_SIZE); - EXPECT_EQ(0, result); - std::size_t message_size = std::strlen(message); - EXPECT_GE(BUFFER_SIZE - 1u, message_size); - EXPECT_EQ(get_system_error(error_code), message); - - // safe_strerror never uses buffer on MinGW. -#ifndef __MINGW32__ - result = safe_strerror(error_code, message = buffer, message_size); - EXPECT_EQ(ERANGE, result); - result = safe_strerror(error_code, message = buffer, 1); - EXPECT_EQ(buffer, message); // Message should point to buffer. - EXPECT_EQ(ERANGE, result); - EXPECT_STREQ("", message); -#endif -} - typedef void (*FormatErrorMessage)( fmt::Writer &out, int error_code, StringRef message); diff --git a/test/util.cc b/test/util.cc index 216b81ac..c4d4e5d9 100644 --- a/test/util.cc +++ b/test/util.cc @@ -37,3 +37,16 @@ void increment(char *s) { s[i] = '0'; } } + +std::string get_system_error(int error_code) { +#if defined(__MINGW32__) || !defined(_WIN32) + return strerror(error_code); +#else + enum { BUFFER_SIZE = 200 }; + char buffer[BUFFER_SIZE]; + EXPECT_EQ(0, strerror_s(buffer, BUFFER_SIZE, error_code)); + std::size_t max_len = BUFFER_SIZE - 1; + EXPECT_LT(std::strlen(buffer), max_len); + return buffer; +#endif +} diff --git a/test/util.h b/test/util.h index 160179b3..fe84a906 100644 --- a/test/util.h +++ b/test/util.h @@ -27,6 +27,7 @@ #include #include +#include enum {BUFFER_SIZE = 256}; @@ -46,3 +47,5 @@ void safe_sprintf(char (&buffer)[SIZE], const char *format, ...) { // Increment a number in a string. void increment(char *s); + +std::string get_system_error(int error_code);