Throw SystemError if fwrite fails.
This commit is contained in:
parent
4e33311597
commit
859a4975f6
@ -100,7 +100,7 @@ using fmt::pad;
|
||||
} \
|
||||
catch (expected_exception const& e) { \
|
||||
gtest_caught_expected = true; \
|
||||
if (std::strcmp(message, e.what()) != 0) \
|
||||
if (std::string(message) != e.what()) \
|
||||
throw; \
|
||||
} \
|
||||
catch (...) { \
|
||||
@ -1561,6 +1561,18 @@ TEST(FormatterTest, FileSink) {
|
||||
EXPECT_EQ("test", ReadFile("out"));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, FileSinkWriteError) {
|
||||
FILE *f = std::fopen("out", "r");
|
||||
fmt::FileSink fs(f);
|
||||
int result = std::fwrite(" ", 1, 1, f);
|
||||
int error_code = errno;
|
||||
EXPECT_EQ(0, result);
|
||||
std::string error_message =
|
||||
str(Format("{}: {}") << "cannot write to file" << strerror(error_code));
|
||||
EXPECT_THROW_MSG(fs(Writer() << "test"), fmt::SystemError, error_message);
|
||||
std::fclose(f);
|
||||
}
|
||||
|
||||
// The test doesn't compile on older compilers which follow C++03 and
|
||||
// require an accessible copy constructor when binding a temporary to
|
||||
// a const reference.
|
||||
|
61
format.cc
61
format.cc
@ -33,11 +33,17 @@
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
using fmt::ULongLong;
|
||||
|
||||
#if _MSC_VER
|
||||
@ -161,6 +167,55 @@ void fmt::internal::ReportUnknownType(char code, const char *type) {
|
||||
<< static_cast<unsigned>(code) << type));
|
||||
}
|
||||
|
||||
void fmt::internal::FormatSystemErrorMessage(
|
||||
fmt::Writer &out, int error_code, fmt::StringRef message) {
|
||||
#ifndef _WIN32
|
||||
Array<char, INLINE_BUFFER_SIZE> 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
|
||||
if (errno == 0)
|
||||
break;
|
||||
if (errno != ERANGE) {
|
||||
// Can't get error message, report error code instead.
|
||||
out << message << ": error code = " << error_code;
|
||||
return;
|
||||
}
|
||||
buffer.resize(buffer.size() * 2);
|
||||
}
|
||||
out << message << ": " << system_message;
|
||||
#else
|
||||
class String {
|
||||
private:
|
||||
LPWSTR str_;
|
||||
|
||||
public:
|
||||
String() : str_() {}
|
||||
~String() { LocalFree(str_); }
|
||||
LPWSTR *ptr() { return &str_; }
|
||||
LPCWSTR c_str() const { return str_; }
|
||||
};
|
||||
String system_message;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
|
||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
|
||||
UTF16ToUTF8 utf8_message;
|
||||
if (!utf8_message.Convert(system_message.c_str())) {
|
||||
out << message << ": " << utf8_message;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Can't get error message, report error code instead.
|
||||
out << message << ": error code = " << error_code;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Fills the padding around the content and returns the pointer to the
|
||||
// content area.
|
||||
@ -688,6 +743,12 @@ void fmt::BasicWriter<Char>::FormatParser::Format(
|
||||
writer.buffer_.append(start, s);
|
||||
}
|
||||
|
||||
void fmt::SystemErrorSink::operator()(const fmt::Writer &w) const {
|
||||
Writer message;
|
||||
internal::FormatSystemErrorMessage(message, error_code_, w.c_str());
|
||||
throw SystemError(message.c_str(), error_code_);
|
||||
}
|
||||
|
||||
void fmt::ANSITerminalSink::operator()(
|
||||
const fmt::BasicWriter<char> &w) const {
|
||||
char escape[] = "\x1b[30m";
|
||||
|
172
format.h
172
format.h
@ -31,6 +31,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstddef> // for std::ptrdiff_t
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
@ -120,11 +121,71 @@ FMT_GCC_EXTENSION typedef unsigned long long ULongLong;
|
||||
template <typename Char>
|
||||
class BasicWriter;
|
||||
|
||||
typedef BasicWriter<char> Writer;
|
||||
typedef BasicWriter<wchar_t> WWriter;
|
||||
|
||||
template <typename Char>
|
||||
class BasicFormatter;
|
||||
|
||||
struct FormatSpec;
|
||||
|
||||
/**
|
||||
\rst
|
||||
A string reference. It can be constructed from a C string, ``std::string``
|
||||
or as a result of a formatting operation. It is most useful as a parameter
|
||||
type to allow passing different types of strings in a function, for example::
|
||||
|
||||
Formatter<> Format(StringRef format);
|
||||
|
||||
Format("{}") << 42;
|
||||
Format(std::string("{}")) << 42;
|
||||
Format(Format("{{}}")) << 42;
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char>
|
||||
class BasicStringRef {
|
||||
private:
|
||||
const Char *data_;
|
||||
mutable std::size_t size_;
|
||||
|
||||
public:
|
||||
/**
|
||||
Constructs a string reference object from a C string and a size.
|
||||
If *size* is zero, which is the default, the size is computed with
|
||||
`strlen`.
|
||||
*/
|
||||
BasicStringRef(const Char *s, std::size_t size = 0) : data_(s), size_(size) {}
|
||||
|
||||
/**
|
||||
Constructs a string reference from an `std::string` object.
|
||||
*/
|
||||
BasicStringRef(const std::basic_string<Char> &s)
|
||||
: data_(s.c_str()), size_(s.size()) {}
|
||||
|
||||
/**
|
||||
Converts a string reference to an `std::string` object.
|
||||
*/
|
||||
operator std::basic_string<Char>() const {
|
||||
return std::basic_string<Char>(data_, size());
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the pointer to a C string.
|
||||
*/
|
||||
const Char *c_str() const { return data_; }
|
||||
|
||||
/**
|
||||
Returns the string size.
|
||||
*/
|
||||
std::size_t size() const {
|
||||
if (size_ == 0) size_ = std::char_traits<Char>::length(data_);
|
||||
return size_;
|
||||
}
|
||||
};
|
||||
|
||||
typedef BasicStringRef<char> StringRef;
|
||||
typedef BasicStringRef<wchar_t> WStringRef;
|
||||
|
||||
namespace internal {
|
||||
|
||||
// The number of characters to store in the Array object, representing the
|
||||
@ -396,71 +457,36 @@ void FormatDecimal(Char *buffer, UInt value, unsigned num_digits) {
|
||||
template <typename Char, typename T>
|
||||
void FormatCustomArg(
|
||||
BasicWriter<Char> &w, const void *arg, const FormatSpec &spec);
|
||||
}
|
||||
|
||||
// Formats a system error message writing the output to out.
|
||||
void FormatSystemErrorMessage(Writer &out, int error_code, StringRef message);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
\rst
|
||||
A string reference. It can be constructed from a C string, ``std::string``
|
||||
or as a result of a formatting operation. It is most useful as a parameter
|
||||
type to allow passing different types of strings in a function, for example::
|
||||
|
||||
Formatter<> Format(StringRef format);
|
||||
|
||||
Format("{}") << 42;
|
||||
Format(std::string("{}")) << 42;
|
||||
Format(Format("{{}}")) << 42;
|
||||
\endrst
|
||||
A formatting error such as invalid format string.
|
||||
*/
|
||||
template <typename Char>
|
||||
class BasicStringRef {
|
||||
private:
|
||||
const Char *data_;
|
||||
mutable std::size_t size_;
|
||||
|
||||
public:
|
||||
/**
|
||||
Constructs a string reference object from a C string and a size.
|
||||
If *size* is zero, which is the default, the size is computed with
|
||||
`strlen`.
|
||||
*/
|
||||
BasicStringRef(const Char *s, std::size_t size = 0) : data_(s), size_(size) {}
|
||||
|
||||
/**
|
||||
Constructs a string reference from an `std::string` object.
|
||||
*/
|
||||
BasicStringRef(const std::basic_string<Char> &s)
|
||||
: data_(s.c_str()), size_(s.size()) {}
|
||||
|
||||
/**
|
||||
Converts a string reference to an `std::string` object.
|
||||
*/
|
||||
operator std::basic_string<Char>() const {
|
||||
return std::basic_string<Char>(data_, size());
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the pointer to a C string.
|
||||
*/
|
||||
const Char *c_str() const { return data_; }
|
||||
|
||||
/**
|
||||
Returns the string size.
|
||||
*/
|
||||
std::size_t size() const {
|
||||
if (size_ == 0) size_ = std::char_traits<Char>::length(data_);
|
||||
return size_;
|
||||
}
|
||||
};
|
||||
|
||||
typedef BasicStringRef<char> StringRef;
|
||||
typedef BasicStringRef<wchar_t> WStringRef;
|
||||
|
||||
class FormatError : public std::runtime_error {
|
||||
public:
|
||||
explicit FormatError(const std::string &message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
/**
|
||||
An error returned by the operating system or the language runtime,
|
||||
for example a file opening error.
|
||||
*/
|
||||
class SystemError : public std::runtime_error {
|
||||
private:
|
||||
int error_code_;
|
||||
|
||||
public:
|
||||
SystemError(StringRef message, int error_code)
|
||||
: std::runtime_error(message), error_code_(error_code) {}
|
||||
|
||||
int error_code() const { return error_code_; }
|
||||
};
|
||||
|
||||
enum Alignment {
|
||||
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
||||
};
|
||||
@ -1197,9 +1223,6 @@ BasicFormatter<Char> BasicWriter<Char>::Format(StringRef format) {
|
||||
return f;
|
||||
}
|
||||
|
||||
typedef BasicWriter<char> Writer;
|
||||
typedef BasicWriter<wchar_t> WWriter;
|
||||
|
||||
// The default formatting function.
|
||||
template <typename Char, typename T>
|
||||
void Format(BasicWriter<Char> &w, const FormatSpec &spec, const T &value) {
|
||||
@ -1323,15 +1346,14 @@ template <typename Char>
|
||||
inline const Char *c_str(const BasicWriter<Char> &f) { return f.c_str(); }
|
||||
|
||||
/**
|
||||
Returns the content of the output buffer as an `std::string`.
|
||||
Converts a string reference an `std::string`.
|
||||
*/
|
||||
inline std::string str(StringRef s) {
|
||||
return std::string(s.c_str(), s.size());
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a pointer to the output buffer content with terminating null
|
||||
character appended.
|
||||
Returns the pointer to a C string.
|
||||
*/
|
||||
inline const char *c_str(StringRef s) {
|
||||
return s.c_str();
|
||||
@ -1468,6 +1490,28 @@ inline Formatter<NullSink, wchar_t> Format(WStringRef format) {
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
A sink that gets the system error message corresponding to the error code
|
||||
and throws SystemError.
|
||||
*/
|
||||
class SystemErrorSink {
|
||||
private:
|
||||
int error_code_;
|
||||
|
||||
public:
|
||||
explicit SystemErrorSink(int error_code) : error_code_(error_code) {}
|
||||
|
||||
void operator()(const Writer &w) const;
|
||||
};
|
||||
|
||||
/** Throws SystemError with a code and a formatted message. */
|
||||
inline Formatter<SystemErrorSink> ThrowSystemError(
|
||||
int error_code, StringRef format) {
|
||||
Formatter<SystemErrorSink> f(format, SystemErrorSink(error_code));
|
||||
return f;
|
||||
}
|
||||
|
||||
/** A sink that writes output to a file. */
|
||||
class FileSink {
|
||||
private:
|
||||
@ -1478,8 +1522,8 @@ class FileSink {
|
||||
|
||||
/** Writes the output to a file. */
|
||||
void operator()(const BasicWriter<char> &w) const {
|
||||
// TODO: check error
|
||||
std::fwrite(w.data(), w.size(), 1, file_);
|
||||
if (std::fwrite(w.data(), w.size(), 1, file_) == 0)
|
||||
ThrowSystemError(errno, "cannot write to file");
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user