Throw SystemError if fwrite fails.

This commit is contained in:
Victor Zverovich 2014-04-30 06:55:21 -07:00
parent 4e33311597
commit 859a4975f6
3 changed files with 182 additions and 65 deletions

View File

@ -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.

View File

@ -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
View File

@ -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");
}
};