forked from fmtlib/fmt
Throw SystemError if fwrite fails.
This commit is contained in:
@ -100,7 +100,7 @@ using fmt::pad;
|
|||||||
} \
|
} \
|
||||||
catch (expected_exception const& e) { \
|
catch (expected_exception const& e) { \
|
||||||
gtest_caught_expected = true; \
|
gtest_caught_expected = true; \
|
||||||
if (std::strcmp(message, e.what()) != 0) \
|
if (std::string(message) != e.what()) \
|
||||||
throw; \
|
throw; \
|
||||||
} \
|
} \
|
||||||
catch (...) { \
|
catch (...) { \
|
||||||
@ -1561,6 +1561,18 @@ TEST(FormatterTest, FileSink) {
|
|||||||
EXPECT_EQ("test", ReadFile("out"));
|
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
|
// The test doesn't compile on older compilers which follow C++03 and
|
||||||
// require an accessible copy constructor when binding a temporary to
|
// require an accessible copy constructor when binding a temporary to
|
||||||
// a const reference.
|
// a const reference.
|
||||||
|
61
format.cc
61
format.cc
@ -33,11 +33,17 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using fmt::ULongLong;
|
using fmt::ULongLong;
|
||||||
|
|
||||||
#if _MSC_VER
|
#if _MSC_VER
|
||||||
@ -161,6 +167,55 @@ void fmt::internal::ReportUnknownType(char code, const char *type) {
|
|||||||
<< static_cast<unsigned>(code) << 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
|
// Fills the padding around the content and returns the pointer to the
|
||||||
// content area.
|
// content area.
|
||||||
@ -688,6 +743,12 @@ void fmt::BasicWriter<Char>::FormatParser::Format(
|
|||||||
writer.buffer_.append(start, s);
|
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()(
|
void fmt::ANSITerminalSink::operator()(
|
||||||
const fmt::BasicWriter<char> &w) const {
|
const fmt::BasicWriter<char> &w) const {
|
||||||
char escape[] = "\x1b[30m";
|
char escape[] = "\x1b[30m";
|
||||||
|
172
format.h
172
format.h
@ -31,6 +31,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cerrno>
|
||||||
#include <cstddef> // for std::ptrdiff_t
|
#include <cstddef> // for std::ptrdiff_t
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -120,11 +121,71 @@ FMT_GCC_EXTENSION typedef unsigned long long ULongLong;
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
class BasicWriter;
|
class BasicWriter;
|
||||||
|
|
||||||
|
typedef BasicWriter<char> Writer;
|
||||||
|
typedef BasicWriter<wchar_t> WWriter;
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
class BasicFormatter;
|
class BasicFormatter;
|
||||||
|
|
||||||
struct FormatSpec;
|
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 {
|
namespace internal {
|
||||||
|
|
||||||
// The number of characters to store in the Array object, representing the
|
// 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>
|
template <typename Char, typename T>
|
||||||
void FormatCustomArg(
|
void FormatCustomArg(
|
||||||
BasicWriter<Char> &w, const void *arg, const FormatSpec &spec);
|
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 formatting error such as invalid format string.
|
||||||
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;
|
|
||||||
|
|
||||||
class FormatError : public std::runtime_error {
|
class FormatError : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
explicit FormatError(const std::string &message)
|
explicit FormatError(const std::string &message)
|
||||||
: std::runtime_error(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 {
|
enum Alignment {
|
||||||
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
||||||
};
|
};
|
||||||
@ -1197,9 +1223,6 @@ BasicFormatter<Char> BasicWriter<Char>::Format(StringRef format) {
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef BasicWriter<char> Writer;
|
|
||||||
typedef BasicWriter<wchar_t> WWriter;
|
|
||||||
|
|
||||||
// The default formatting function.
|
// The default formatting function.
|
||||||
template <typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void Format(BasicWriter<Char> &w, const FormatSpec &spec, const T &value) {
|
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(); }
|
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) {
|
inline std::string str(StringRef s) {
|
||||||
return std::string(s.c_str(), s.size());
|
return std::string(s.c_str(), s.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a pointer to the output buffer content with terminating null
|
Returns the pointer to a C string.
|
||||||
character appended.
|
|
||||||
*/
|
*/
|
||||||
inline const char *c_str(StringRef s) {
|
inline const char *c_str(StringRef s) {
|
||||||
return s.c_str();
|
return s.c_str();
|
||||||
@ -1468,6 +1490,28 @@ inline Formatter<NullSink, wchar_t> Format(WStringRef format) {
|
|||||||
return f;
|
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. */
|
/** A sink that writes output to a file. */
|
||||||
class FileSink {
|
class FileSink {
|
||||||
private:
|
private:
|
||||||
@ -1478,8 +1522,8 @@ class FileSink {
|
|||||||
|
|
||||||
/** Writes the output to a file. */
|
/** Writes the output to a file. */
|
||||||
void operator()(const BasicWriter<char> &w) const {
|
void operator()(const BasicWriter<char> &w) const {
|
||||||
// TODO: check error
|
if (std::fwrite(w.data(), w.size(), 1, file_) == 0)
|
||||||
std::fwrite(w.data(), w.size(), 1, file_);
|
ThrowSystemError(errno, "cannot write to file");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user