diff --git a/format-test.cc b/format-test.cc index fa5d8ab6..eb0cbbd4 100644 --- a/format-test.cc +++ b/format-test.cc @@ -260,6 +260,7 @@ TEST(UtilTest, StrError) { message = StrError(-1, buffer, BUFFER_SIZE); EXPECT_EQ(0, errno); EXPECT_GE(BUFFER_SIZE - 1, std::strlen(message)); + EXPECT_STREQ(strerror(-1), message); message = StrError(-1, buffer, std::strlen(message)); EXPECT_EQ(ERANGE, errno); } diff --git a/format.cc b/format.cc index d4846f2c..26dab492 100644 --- a/format.cc +++ b/format.cc @@ -224,19 +224,13 @@ char *fmt::internal::StrError( #endif } -void fmt::internal::FormatSystemErrorMessage( +void fmt::internal::FormatCErrorMessage( fmt::Writer &out, int error_code, fmt::StringRef message) { -#ifndef _WIN32 Array buffer; buffer.resize(INLINE_BUFFER_SIZE); char *system_message = 0; for (;;) { - errno = 0; -# ifdef _GNU_SOURCE - system_message = strerror_r(error_code, &buffer[0], buffer.size()); -# else - strerror_r(error_code, system_message = &buffer[0], buffer.size()); -# endif + system_message = StrError(error_code, &buffer[0], buffer.size()); if (errno == 0) break; if (errno != ERANGE) { @@ -247,6 +241,12 @@ void fmt::internal::FormatSystemErrorMessage( buffer.resize(buffer.size() * 2); } out << message << ": " << system_message; +} + +void fmt::internal::FormatSystemErrorMessage( + fmt::Writer &out, int error_code, fmt::StringRef message) { +#ifndef _WIN32 + FormatCErrorMessage(out, error_code, message); #else class String { private: @@ -806,6 +806,12 @@ void fmt::SystemErrorSink::operator()(const fmt::Writer &w) const { throw SystemError(message.c_str(), error_code_); } +void fmt::CErrorSink::operator()(const Writer &w) const { + Writer message; + internal::FormatCErrorMessage(message, error_code_, w.c_str()); + throw SystemError(message.c_str(), error_code_); +} + void fmt::ANSITerminalSink::operator()( const fmt::BasicWriter &w) const { char escape[] = "\x1b[30m"; diff --git a/format.h b/format.h index d56a5a27..1d2ae189 100644 --- a/format.h +++ b/format.h @@ -499,6 +499,9 @@ class UTF16ToUTF8 { // Buffer should be at least of size 1. char *StrError(int error_code, char *buffer, std::size_t buffer_size); +// Formats a standard C library error message writing the output to out. +void FormatCErrorMessage(Writer &out, int error_code, StringRef message); + // Formats a system error message writing the output to out. void FormatSystemErrorMessage(Writer &out, int error_code, StringRef message); @@ -1520,7 +1523,7 @@ class Formatter : private Sink, public BasicFormatter { See also `Format String Syntax`_. \endrst -*/ + */ inline Formatter<> Format(StringRef format) { Formatter<> f(format); return f; @@ -1532,7 +1535,7 @@ inline Formatter Format(WStringRef format) { } /** - A sink that gets the system error message corresponding to the error code + A sink that gets the error message corresponding to a system error code and throws SystemError. */ class SystemErrorSink { @@ -1545,13 +1548,45 @@ class SystemErrorSink { void operator()(const Writer &w) const; }; -/** Throws SystemError with a code and a formatted message. */ +/** + Formats a message and throws SystemError with the description of the form + ": ", where is the formatted message and + is the system message corresponding to the error code. + error_code is a system error code as given by errno for POSIX and + GetLastError for Windows. + */ inline Formatter ThrowSystemError( - int error_code, StringRef format = 0) { + int error_code, StringRef format) { Formatter f(format, SystemErrorSink(error_code)); return f; } +/** + A sink that gets the error message corresponding to a standard C library + error code as given by errno and throws SystemError. + */ +class CErrorSink { + private: + int error_code_; + + public: + explicit CErrorSink(int error_code) : error_code_(error_code) {} + + void operator()(const Writer &w) const; +}; + +/** + Formats a message and throws SystemError with the description of the form + ": ", where is the formatted message and + is the system message corresponding to the error code. + error_code is an error code as given by errno after calling a C library + function. + */ +inline Formatter ThrowCError(int error_code, StringRef format) { + Formatter f(format, CErrorSink(error_code)); + return f; +} + /** A sink that writes output to a file. */ class FileSink { private: @@ -1563,7 +1598,7 @@ class FileSink { /** Writes the output to a file. */ void operator()(const BasicWriter &w) const { if (std::fwrite(w.data(), w.size(), 1, file_) == 0) - ThrowSystemError(errno, "cannot write to file"); + ThrowCError(errno, "cannot write to file"); } };