Merge branch 'master' of https://github.com/fmtlib/fmt
This commit is contained in:
commit
3e9c0d5879
145
ChangeLog.rst
145
ChangeLog.rst
@ -12,7 +12,28 @@
|
|||||||
of the library for standardization in `P0645R2 Text Formatting
|
of the library for standardization in `P0645R2 Text Formatting
|
||||||
<https://wg21.link/P0645>`_.
|
<https://wg21.link/P0645>`_.
|
||||||
|
|
||||||
* Implemented ``constexpr`` parsing of format strings.
|
* Implemented ``constexpr`` parsing of format strings and `compile-time format
|
||||||
|
string checks
|
||||||
|
<http://fmtlib.net/dev/api.html#compile-time-format-string-checks>`_. For
|
||||||
|
example
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
std::string s = format(fmt("{:d}"), "foo");
|
||||||
|
|
||||||
|
gives a compile-time error because ``d`` is an invalid specifier for strings
|
||||||
|
(`godbolt <https://godbolt.org/g/rnCy9Q>`_)::
|
||||||
|
|
||||||
|
...
|
||||||
|
<source>:4:19: note: in instantiation of function template specialization 'fmt::v5::format<S, char [4]>' requested here
|
||||||
|
std::string s = format(fmt("{:d}"), "foo");
|
||||||
|
^
|
||||||
|
format.h:1337:13: note: non-constexpr function 'on_error' cannot be used in a constant expression
|
||||||
|
handler.on_error("invalid type specifier");
|
||||||
|
|
||||||
|
Compile-time checks require relaxed ``constexpr`` (C++14 feature) support. If
|
||||||
|
the latter is not available, checks will be performed at runtime.
|
||||||
|
|
||||||
* Separated format string parsing and formatting in the extension API to enable
|
* Separated format string parsing and formatting in the extension API to enable
|
||||||
compile-time format string processing. For example
|
compile-time format string processing. For example
|
||||||
@ -45,7 +66,7 @@
|
|||||||
|
|
||||||
std::string s = format(fmt("{:x}"), S());
|
std::string s = format(fmt("{:x}"), S());
|
||||||
|
|
||||||
will give a compile-time error due to invalid format specifier (`godbolt
|
gives a compile-time error due to invalid format specifier (`godbolt
|
||||||
<https://godbolt.org/g/ywhrPp>`_)::
|
<https://godbolt.org/g/ywhrPp>`_)::
|
||||||
|
|
||||||
...
|
...
|
||||||
@ -63,9 +84,20 @@
|
|||||||
std::vector<char> out;
|
std::vector<char> out;
|
||||||
fmt::format_to(std::back_inserter(out), "{}", 42);
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||||
|
|
||||||
|
* Added the `format_to_n
|
||||||
|
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
|
||||||
|
function that restricts the output to the specified number of characters
|
||||||
|
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
char out[4];
|
||||||
|
fmt::format_to_n(out, sizeof(out), "{}", 12345);
|
||||||
|
// out == "1234" (without terminating '\0')
|
||||||
|
|
||||||
* Added the `formatted_size
|
* Added the `formatted_size
|
||||||
<http://fmtlib.net/dev/api.html#output-iterator-support>`_ function for
|
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
|
||||||
computing output size:
|
function for computing the output size:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@ -101,6 +133,9 @@
|
|||||||
vreport_error(format, fmt::make_format_args(args...));
|
vreport_error(format, fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* Added the ``make_printf_args`` function for capturing ``printf`` arguments.
|
||||||
|
Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_.
|
||||||
|
|
||||||
* Added prefix ``v`` to non-variadic functions taking ``format_args`` to
|
* Added prefix ``v`` to non-variadic functions taking ``format_args`` to
|
||||||
distinguish them from variadic ones:
|
distinguish them from variadic ones:
|
||||||
|
|
||||||
@ -141,30 +176,46 @@
|
|||||||
* Disallowed formatting of multibyte strings into a wide character target
|
* Disallowed formatting of multibyte strings into a wide character target
|
||||||
(`#606 <https://github.com/fmtlib/fmt/pull/606>`_).
|
(`#606 <https://github.com/fmtlib/fmt/pull/606>`_).
|
||||||
|
|
||||||
* Added a section describing `the use of header-only target with CMake
|
* Improved documentation (
|
||||||
<http://fmtlib.net/dev/usage.html#header-only-usage-with-cmake>`_ to the docs
|
`#515 <https://github.com/fmtlib/fmt/pull/515>`_,
|
||||||
(`#515 <https://github.com/fmtlib/fmt/pull/515>`_).
|
|
||||||
Thanks `@ibell (Ian Bell) <https://github.com/ibell>`_.
|
|
||||||
|
|
||||||
* Added a `note about errno <http://fmtlib.net/latest/index.html#safety>`_ to the
|
|
||||||
documentation (
|
|
||||||
`#614 <https://github.com/fmtlib/fmt/issues/614>`_,
|
`#614 <https://github.com/fmtlib/fmt/issues/614>`_,
|
||||||
`#617 <https://github.com/fmtlib/fmt/pull/617>`_).
|
`#617 <https://github.com/fmtlib/fmt/pull/617>`_,
|
||||||
Thanks `@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_.
|
`#661 <https://github.com/fmtlib/fmt/pull/661>`_,
|
||||||
|
`#680 <https://github.com/fmtlib/fmt/pull/680>`_).
|
||||||
|
Thanks `@ibell (Ian Bell) <https://github.com/ibell>`_,
|
||||||
|
`@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_, and
|
||||||
|
`@johnthagen <https://github.com/johnthagen>`_.
|
||||||
|
|
||||||
* Implemented more efficient handling of large number of format arguments.
|
* Implemented more efficient handling of large number of format arguments.
|
||||||
|
|
||||||
|
* Added debug postfix ``d`` to the `fmt`` library name
|
||||||
|
(`#636 <https://github.com/fmtlib/fmt/issues/636>`_).
|
||||||
|
|
||||||
* Removed unnecessary ``fmt/`` prefix in includes
|
* Removed unnecessary ``fmt/`` prefix in includes
|
||||||
(`#397 <https://github.com/fmtlib/fmt/pull/397>`_).
|
(`#397 <https://github.com/fmtlib/fmt/pull/397>`_).
|
||||||
Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_.
|
Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_.
|
||||||
|
|
||||||
* Renamed ``CHAR_WIDTH`` to ``CHAR_SIZE`` to avoid collision with ISO/IEC TS
|
* Moved ``fmt/*.h`` to ``include/fmt/*.h`` to prevent irrelevant files and
|
||||||
18661-1:2014 macro.
|
directories appearing on the include search paths when fmt is used as a
|
||||||
|
subproject and moved source files to the ``src`` directory.
|
||||||
|
|
||||||
|
* Added qmake project file ``support/fmt.pro``
|
||||||
|
(`#641 <https://github.com/fmtlib/fmt/pull/641>`_).
|
||||||
|
Thanks `@cowo78 (Giuseppe Corbelli) <https://github.com/cowo78>`_.
|
||||||
|
|
||||||
|
* Added Gradle build file ``support/build.gradle``
|
||||||
|
(`#649 <https://github.com/fmtlib/fmt/pull/649>`_).
|
||||||
|
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_.
|
||||||
|
|
||||||
|
* Removed ``FMT_CPPFORMAT`` CMake option.
|
||||||
|
|
||||||
* Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc
|
* Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc
|
||||||
(`#616 <https://github.com/fmtlib/fmt/pull/616>`_).
|
(`#616 <https://github.com/fmtlib/fmt/pull/616>`_).
|
||||||
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
|
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
|
||||||
|
|
||||||
|
* Fixed handling of nested braces in ``fmt::join``
|
||||||
|
(`#638 <https://github.com/fmtlib/fmt/issues/638>`_).
|
||||||
|
|
||||||
* Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5
|
* Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5
|
||||||
(`#497 <https://github.com/fmtlib/fmt/pull/497>`_).
|
(`#497 <https://github.com/fmtlib/fmt/pull/497>`_).
|
||||||
Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_.
|
Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_.
|
||||||
@ -173,6 +224,68 @@
|
|||||||
(`#626 <https://github.com/fmtlib/fmt/pull/626>`_).
|
(`#626 <https://github.com/fmtlib/fmt/pull/626>`_).
|
||||||
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
|
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
|
||||||
|
|
||||||
|
* Fixed various compiler warnings (
|
||||||
|
`#640 <https://github.com/fmtlib/fmt/pull/640>`_,
|
||||||
|
`#656 <https://github.com/fmtlib/fmt/pull/656>`_,
|
||||||
|
`#679 <https://github.com/fmtlib/fmt/pull/679>`_,
|
||||||
|
`#681 <https://github.com/fmtlib/fmt/pull/681>`_,
|
||||||
|
`#705 <https://github.com/fmtlib/fmt/pull/705>`_).
|
||||||
|
Thanks `@peterbell10 <https://github.com/peterbell10>`_,
|
||||||
|
`@LarsGullik <https://github.com/LarsGullik>`_,
|
||||||
|
`@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
|
||||||
|
`@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_, and
|
||||||
|
`@christianparpart (Christian Parpart) <https://github.com/christianparpart>`_.
|
||||||
|
|
||||||
|
* Worked around an MSVC bug and fixed several warnings
|
||||||
|
(`#653 <https://github.com/fmtlib/fmt/pull/653>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
|
* Worked around GCC bug 67371
|
||||||
|
(`#682 <https://github.com/fmtlib/fmt/issues/682>`_).
|
||||||
|
|
||||||
|
* Fixed compilation with ``-fno-exceptions``
|
||||||
|
(`#655 <https://github.com/fmtlib/fmt/pull/655>`_).
|
||||||
|
Thanks `@chenxiaolong (Andrew Gunnerson) <https://github.com/chenxiaolong>`_.
|
||||||
|
|
||||||
|
* Made ``constexpr remove_prefix`` gcc version check tighter
|
||||||
|
(`#648 <https://github.com/fmtlib/fmt/issues/648>`_).
|
||||||
|
|
||||||
|
* Renamed internal type enum constants to prevent collision with poorly written
|
||||||
|
C libraries (`#644 <https://github.com/fmtlib/fmt/issues/644>`_).
|
||||||
|
|
||||||
|
* Added detection of ``wostream operator<<``
|
||||||
|
(`#650 <https://github.com/fmtlib/fmt/issues/650>`_).
|
||||||
|
|
||||||
|
* Fixed compilation on OpenBSD
|
||||||
|
(`#660 <https://github.com/fmtlib/fmt/pull/660>`_).
|
||||||
|
Thanks `@hubslave <https://github.com/hubslave>`_.
|
||||||
|
|
||||||
|
* Fixed compilation when there is a mismatch between ``-std`` options between
|
||||||
|
the library and user code
|
||||||
|
(`#664 <https://github.com/fmtlib/fmt/issues/664>`_).
|
||||||
|
|
||||||
|
* Improved generated binary code on GCC 7 and older
|
||||||
|
(`#668 <https://github.com/fmtlib/fmt/issues/668>`_).
|
||||||
|
|
||||||
|
* Fixed handling of numeric alignment with no width
|
||||||
|
(`#675 <https://github.com/fmtlib/fmt/issues/675>`_).
|
||||||
|
|
||||||
|
* Fixed handling of empty strings in UTF8/16 converters
|
||||||
|
(`#676 <https://github.com/fmtlib/fmt/pull/676>`_).
|
||||||
|
Thanks `@vgalka-sl (Vasili Galka) <https://github.com/vgalka-sl>`_.
|
||||||
|
|
||||||
|
* Fixed formatting of an empty ``string_view``
|
||||||
|
(`#689 <https://github.com/fmtlib/fmt/issues/689>`_).
|
||||||
|
|
||||||
|
* Fixed detection of ``string_view`` on libc++
|
||||||
|
(`#686 <https://github.com/fmtlib/fmt/issues/686>`_).
|
||||||
|
|
||||||
|
* Fixed DLL issues (`#696 <https://github.com/fmtlib/fmt/pull/696>`_).
|
||||||
|
Thanks `@sebkoenig <https://github.com/sebkoenig>`_.
|
||||||
|
|
||||||
|
* Fixed compile checks for mixing narrow and wide strings
|
||||||
|
(`#690 <https://github.com/fmtlib/fmt/issues/690>`_).
|
||||||
|
|
||||||
4.1.0 - 2017-12-20
|
4.1.0 - 2017-12-20
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -234,7 +347,7 @@
|
|||||||
`@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and
|
`@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and
|
||||||
`@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
|
`@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
|
||||||
|
|
||||||
* Improved CMake: Used GNUInstallDirs to set installation location
|
* Improved CMake: Used ``GNUInstallDirs`` to set installation location
|
||||||
(`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings
|
(`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings
|
||||||
(`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and
|
(`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and
|
||||||
`#556 <https://github.com/fmtlib/fmt/pull/556>`_).
|
`#556 <https://github.com/fmtlib/fmt/pull/556>`_).
|
||||||
|
|||||||
@ -629,8 +629,9 @@ inline typename std::enable_if<
|
|||||||
template <typename C, typename T, typename Char = typename C::char_type>
|
template <typename C, typename T, typename Char = typename C::char_type>
|
||||||
inline typename std::enable_if<
|
inline typename std::enable_if<
|
||||||
!convert_to_int<T, Char>::value &&
|
!convert_to_int<T, Char>::value &&
|
||||||
!std::is_convertible<T, basic_string_view<Char>>::value &&
|
!std::is_convertible<T, basic_string_view<Char>>::value,
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value,
|
// Implicit conversion to std::string is not handled here because it's
|
||||||
|
// unsafe: https://github.com/fmtlib/fmt/issues/729
|
||||||
typed_value<C, custom_type>>::type
|
typed_value<C, custom_type>>::type
|
||||||
make_value(const T &val) { return val; }
|
make_value(const T &val) { return val; }
|
||||||
|
|
||||||
|
|||||||
@ -113,14 +113,14 @@ int safe_strerror(
|
|||||||
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
||||||
|
|
||||||
class StrError {
|
class dispatcher {
|
||||||
private:
|
private:
|
||||||
int error_code_;
|
int error_code_;
|
||||||
char *&buffer_;
|
char *&buffer_;
|
||||||
std::size_t buffer_size_;
|
std::size_t buffer_size_;
|
||||||
|
|
||||||
// A noop assignment operator to avoid bogus warnings.
|
// A noop assignment operator to avoid bogus warnings.
|
||||||
void operator=(const StrError &) {}
|
void operator=(const dispatcher &) {}
|
||||||
|
|
||||||
// Handle the result of XSI-compliant version of strerror_r.
|
// Handle the result of XSI-compliant version of strerror_r.
|
||||||
int handle(int result) {
|
int handle(int result) {
|
||||||
@ -157,14 +157,14 @@ int safe_strerror(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StrError(int err_code, char *&buf, std::size_t buf_size)
|
dispatcher(int err_code, char *&buf, std::size_t buf_size)
|
||||||
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return StrError(error_code, buffer, buffer_size).run();
|
return dispatcher(error_code, buffer, buffer_size).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void format_error_code(internal::buffer &out, int error_code,
|
void format_error_code(internal::buffer &out, int error_code,
|
||||||
|
|||||||
@ -2974,7 +2974,7 @@ FMT_API void report_windows_error(int error_code,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Fast integer formatter. */
|
/** Fast integer formatter. */
|
||||||
class FormatInt {
|
class format_int {
|
||||||
private:
|
private:
|
||||||
// Buffer should be large enough to hold all digits (digits10 + 1),
|
// Buffer should be large enough to hold all digits (digits10 + 1),
|
||||||
// a sign and a null character.
|
// a sign and a null character.
|
||||||
@ -3015,12 +3015,12 @@ class FormatInt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FormatInt(int value) { format_signed(value); }
|
explicit format_int(int value) { format_signed(value); }
|
||||||
explicit FormatInt(long value) { format_signed(value); }
|
explicit format_int(long value) { format_signed(value); }
|
||||||
explicit FormatInt(long long value) { format_signed(value); }
|
explicit format_int(long long value) { format_signed(value); }
|
||||||
explicit FormatInt(unsigned value) : str_(format_decimal(value)) {}
|
explicit format_int(unsigned value) : str_(format_decimal(value)) {}
|
||||||
explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}
|
explicit format_int(unsigned long value) : str_(format_decimal(value)) {}
|
||||||
explicit FormatInt(unsigned long long value) : str_(format_decimal(value)) {}
|
explicit format_int(unsigned long long value) : str_(format_decimal(value)) {}
|
||||||
|
|
||||||
/** Returns the number of characters written to the output buffer. */
|
/** Returns the number of characters written to the output buffer. */
|
||||||
std::size_t size() const {
|
std::size_t size() const {
|
||||||
@ -3647,7 +3647,7 @@ FMT_END_NAMESPACE
|
|||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
// A compile-time error because 'd' is an invalid specifier for strings.
|
// A compile-time error because 'd' is an invalid specifier for strings.
|
||||||
std::string s = fmt::format(fmt("{:d}"), "foo");
|
std::string s = format(fmt("{:d}"), "foo");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
# define fmt(s) FMT_STRING(s)
|
# define fmt(s) FMT_STRING(s)
|
||||||
|
|||||||
@ -15,7 +15,7 @@ FMT_BEGIN_NAMESPACE
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template <class Char>
|
template <class Char>
|
||||||
class FormatBuf : public std::basic_streambuf<Char> {
|
class formatbuf : public std::basic_streambuf<Char> {
|
||||||
private:
|
private:
|
||||||
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||||
@ -23,7 +23,7 @@ class FormatBuf : public std::basic_streambuf<Char> {
|
|||||||
basic_buffer<Char> &buffer_;
|
basic_buffer<Char> &buffer_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FormatBuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// The put-area is actually always empty. This makes the implementation
|
// The put-area is actually always empty. This makes the implementation
|
||||||
@ -90,7 +90,7 @@ void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
|||||||
|
|
||||||
template <typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||||
internal::FormatBuf<Char> format_buf(buffer);
|
internal::formatbuf<Char> format_buf(buffer);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
output << value;
|
output << value;
|
||||||
|
|||||||
@ -113,31 +113,31 @@ typedef basic_cstring_view<char> cstring_view;
|
|||||||
typedef basic_cstring_view<wchar_t> wcstring_view;
|
typedef basic_cstring_view<wchar_t> wcstring_view;
|
||||||
|
|
||||||
// An error code.
|
// An error code.
|
||||||
class ErrorCode {
|
class error_code {
|
||||||
private:
|
private:
|
||||||
int value_;
|
int value_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||||
|
|
||||||
int get() const FMT_NOEXCEPT { return value_; }
|
int get() const FMT_NOEXCEPT { return value_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class BufferedFile {
|
class buffered_file {
|
||||||
private:
|
private:
|
||||||
FILE *file_;
|
FILE *file_;
|
||||||
|
|
||||||
friend class File;
|
friend class file;
|
||||||
|
|
||||||
explicit BufferedFile(FILE *f) : file_(f) {}
|
explicit buffered_file(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructs a BufferedFile object which doesn't represent any file.
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~BufferedFile() FMT_DTOR_NOEXCEPT;
|
FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
@ -152,22 +152,22 @@ class BufferedFile {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// A "move constructor" for moving from a temporary.
|
// A "move constructor" for moving from a temporary.
|
||||||
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
|
buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
// A "move constructor" for moving from an lvalue.
|
||||||
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
|
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) {
|
||||||
f.file_ = FMT_NULL;
|
f.file_ = FMT_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
// A "move assignment operator" for moving from a temporary.
|
||||||
BufferedFile &operator=(Proxy p) {
|
buffered_file &operator=(Proxy p) {
|
||||||
close();
|
close();
|
||||||
file_ = p.file;
|
file_ = p.file;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
// A "move assignment operator" for moving from an lvalue.
|
||||||
BufferedFile &operator=(BufferedFile &other) {
|
buffered_file &operator=(buffered_file &other) {
|
||||||
close();
|
close();
|
||||||
file_ = other.file_;
|
file_ = other.file_;
|
||||||
other.file_ = FMT_NULL;
|
other.file_ = FMT_NULL;
|
||||||
@ -175,7 +175,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
// Returns a proxy object for moving from a temporary:
|
||||||
// BufferedFile file = BufferedFile(...);
|
// buffered_file file = buffered_file(...);
|
||||||
operator Proxy() FMT_NOEXCEPT {
|
operator Proxy() FMT_NOEXCEPT {
|
||||||
Proxy p = {file_};
|
Proxy p = {file_};
|
||||||
file_ = FMT_NULL;
|
file_ = FMT_NULL;
|
||||||
@ -184,14 +184,14 @@ public:
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
private:
|
private:
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
|
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
|
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
|
||||||
other.file_ = FMT_NULL;
|
other.file_ = FMT_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedFile& operator=(BufferedFile &&other) {
|
buffered_file& operator=(buffered_file &&other) {
|
||||||
close();
|
close();
|
||||||
file_ = other.file_;
|
file_ = other.file_;
|
||||||
other.file_ = FMT_NULL;
|
other.file_ = FMT_NULL;
|
||||||
@ -200,7 +200,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Opens a file.
|
// Opens a file.
|
||||||
FMT_API BufferedFile(cstring_view filename, cstring_view mode);
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
FMT_API void close();
|
FMT_API void close();
|
||||||
@ -222,18 +222,18 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A file. Closed file is represented by a File object with descriptor -1.
|
// A file. Closed file is represented by a file object with descriptor -1.
|
||||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||||
// fmt::system_error in case of failure. Note that some errors such as
|
// fmt::system_error in case of failure. Note that some errors such as
|
||||||
// closing the file multiple times will cause a crash on Windows rather
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
// than an exception. You can get standard behavior by overriding the
|
// than an exception. You can get standard behavior by overriding the
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
class File {
|
class file {
|
||||||
private:
|
private:
|
||||||
int fd_; // File descriptor.
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
// Constructs a File object with a given descriptor.
|
// Constructs a file object with a given descriptor.
|
||||||
explicit File(int fd) : fd_(fd) {}
|
explicit file(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Possible values for the oflag argument to the constructor.
|
// Possible values for the oflag argument to the constructor.
|
||||||
@ -243,11 +243,11 @@ class File {
|
|||||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a File object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
File() FMT_NOEXCEPT : fd_(-1) {}
|
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
// Opens a file and constructs a File object representing this file.
|
// Opens a file and constructs a file object representing this file.
|
||||||
FMT_API File(cstring_view path, int oflag);
|
FMT_API file(cstring_view path, int oflag);
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
@ -262,22 +262,22 @@ class File {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// A "move constructor" for moving from a temporary.
|
// A "move constructor" for moving from a temporary.
|
||||||
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
// A "move constructor" for moving from an lvalue.
|
||||||
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
|
file(file &other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
// A "move assignment operator" for moving from a temporary.
|
||||||
File &operator=(Proxy p) {
|
file &operator=(Proxy p) {
|
||||||
close();
|
close();
|
||||||
fd_ = p.fd;
|
fd_ = p.fd;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
// A "move assignment operator" for moving from an lvalue.
|
||||||
File &operator=(File &other) {
|
file &operator=(file &other) {
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
@ -285,7 +285,7 @@ class File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
// Returns a proxy object for moving from a temporary:
|
||||||
// File file = File(...);
|
// file f = file(...);
|
||||||
operator Proxy() FMT_NOEXCEPT {
|
operator Proxy() FMT_NOEXCEPT {
|
||||||
Proxy p = {fd_};
|
Proxy p = {fd_};
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
@ -294,14 +294,14 @@ class File {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
private:
|
private:
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(File);
|
FMT_DISALLOW_COPY_AND_ASSIGN(file);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
File& operator=(File &&other) {
|
file& operator=(file &&other) {
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
@ -310,7 +310,7 @@ class File {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~File() FMT_DTOR_NOEXCEPT;
|
FMT_API ~file() FMT_DTOR_NOEXCEPT;
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Returns the file descriptor.
|
||||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
@ -330,7 +330,7 @@ class File {
|
|||||||
|
|
||||||
// Duplicates a file descriptor with the dup function and returns
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
// the duplicate as a file object.
|
// the duplicate as a file object.
|
||||||
FMT_API static File dup(int fd);
|
FMT_API static file dup(int fd);
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
@ -338,15 +338,15 @@ class File {
|
|||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
|
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
|
||||||
|
|
||||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
// and writing respectively.
|
// and writing respectively.
|
||||||
FMT_API static void pipe(File &read_end, File &write_end);
|
FMT_API static void pipe(file &read_end, file &write_end);
|
||||||
|
|
||||||
// Creates a BufferedFile object associated with this file and detaches
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
// this File object from the file.
|
// this file object from the file.
|
||||||
FMT_API BufferedFile fdopen(const char *mode);
|
FMT_API buffered_file fdopen(const char *mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
@ -409,8 +409,8 @@ FMT_END_NAMESPACE
|
|||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
namespace std {
|
namespace std {
|
||||||
// For compatibility with C++98.
|
// For compatibility with C++98.
|
||||||
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; }
|
inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; }
|
||||||
inline fmt::File &move(fmt::File &f) { return f; }
|
inline fmt::file &move(fmt::file &f) { return f; }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -22,11 +22,11 @@ inline null<> gmtime_s(...) { return null<>(); }
|
|||||||
|
|
||||||
// Thread-safe replacement for std::localtime
|
// Thread-safe replacement for std::localtime
|
||||||
inline std::tm localtime(std::time_t time) {
|
inline std::tm localtime(std::time_t time) {
|
||||||
struct LocalTime {
|
struct dispatcher {
|
||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
std::tm tm_;
|
std::tm tm_;
|
||||||
|
|
||||||
LocalTime(std::time_t t): time_(t) {}
|
dispatcher(std::time_t t): time_(t) {}
|
||||||
|
|
||||||
bool run() {
|
bool run() {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::internal;
|
||||||
@ -49,7 +49,7 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
return tm != FMT_NULL;
|
return tm != FMT_NULL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
LocalTime lt(time);
|
dispatcher lt(time);
|
||||||
if (lt.run())
|
if (lt.run())
|
||||||
return lt.tm_;
|
return lt.tm_;
|
||||||
// Too big time values may be unsupported.
|
// Too big time values may be unsupported.
|
||||||
@ -59,11 +59,11 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
// Thread-safe replacement for std::gmtime
|
||||||
inline std::tm gmtime(std::time_t time) {
|
inline std::tm gmtime(std::time_t time) {
|
||||||
struct GMTime {
|
struct dispatcher {
|
||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
std::tm tm_;
|
std::tm tm_;
|
||||||
|
|
||||||
GMTime(std::time_t t): time_(t) {}
|
dispatcher(std::time_t t): time_(t) {}
|
||||||
|
|
||||||
bool run() {
|
bool run() {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::internal;
|
||||||
@ -85,7 +85,7 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
return tm != FMT_NULL;
|
return tm != FMT_NULL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
GMTime gt(time);
|
dispatcher gt(time);
|
||||||
if (gt.run())
|
if (gt.run())
|
||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
// Too big time values may be unsupported.
|
// Too big time values may be unsupported.
|
||||||
@ -94,11 +94,13 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time) {
|
inline std::size_t strftime(char *str, std::size_t count, const char *format,
|
||||||
|
const std::tm *time) {
|
||||||
return std::strftime(str, count, format, time);
|
return std::strftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time) {
|
inline std::size_t strftime(wchar_t *str, std::size_t count,
|
||||||
|
const wchar_t *format, const std::tm *time) {
|
||||||
return std::wcsftime(str, count, format, time);
|
return std::wcsftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,7 +128,8 @@ struct formatter<std::tm, Char> {
|
|||||||
std::size_t start = buf.size();
|
std::size_t start = buf.size();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
std::size_t size = buf.capacity() - start;
|
std::size_t size = buf.capacity() - start;
|
||||||
std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
std::size_t count =
|
||||||
|
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
buf.resize(start + count);
|
buf.resize(start + count);
|
||||||
break;
|
break;
|
||||||
|
|||||||
42
src/posix.cc
42
src/posix.cc
@ -66,19 +66,19 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
|||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
BufferedFile::~BufferedFile() FMT_NOEXCEPT {
|
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||||
report_system_error(errno, "cannot close file");
|
report_system_error(errno, "cannot close file");
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedFile::BufferedFile(cstring_view filename, cstring_view mode) {
|
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||||
FMT_RETRY_VAL(file_,
|
FMT_RETRY_VAL(file_,
|
||||||
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
|
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
|
||||||
if (!file_)
|
if (!file_)
|
||||||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedFile::close() {
|
void buffered_file::close() {
|
||||||
if (!file_)
|
if (!file_)
|
||||||
return;
|
return;
|
||||||
int result = FMT_SYSTEM(fclose(file_));
|
int result = FMT_SYSTEM(fclose(file_));
|
||||||
@ -90,14 +90,14 @@ void BufferedFile::close() {
|
|||||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||||
#define FMT_ARGS
|
#define FMT_ARGS
|
||||||
|
|
||||||
int BufferedFile::fileno() const {
|
int buffered_file::fileno() const {
|
||||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
File::File(cstring_view path, int oflag) {
|
file::file(cstring_view path, int oflag) {
|
||||||
int mode = S_IRUSR | S_IWUSR;
|
int mode = S_IRUSR | S_IWUSR;
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
@ -109,14 +109,14 @@ File::File(cstring_view path, int oflag) {
|
|||||||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
File::~File() FMT_NOEXCEPT {
|
file::~file() FMT_NOEXCEPT {
|
||||||
// Don't retry close in case of EINTR!
|
// Don't retry close in case of EINTR!
|
||||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||||
report_system_error(errno, "cannot close file");
|
report_system_error(errno, "cannot close file");
|
||||||
}
|
}
|
||||||
|
|
||||||
void File::close() {
|
void file::close() {
|
||||||
if (fd_ == -1)
|
if (fd_ == -1)
|
||||||
return;
|
return;
|
||||||
// Don't retry close in case of EINTR!
|
// Don't retry close in case of EINTR!
|
||||||
@ -127,7 +127,7 @@ void File::close() {
|
|||||||
FMT_THROW(system_error(errno, "cannot close file"));
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
long long File::size() const {
|
long long file::size() const {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||||
@ -148,12 +148,12 @@ long long File::size() const {
|
|||||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||||
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||||
"return type of File::size is not large enough");
|
"return type of file::size is not large enough");
|
||||||
return file_stat.st_size;
|
return file_stat.st_size;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t File::read(void *buffer, std::size_t count) {
|
std::size_t file::read(void *buffer, std::size_t count) {
|
||||||
RWResult result = 0;
|
RWResult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
@ -161,7 +161,7 @@ std::size_t File::read(void *buffer, std::size_t count) {
|
|||||||
return internal::to_unsigned(result);
|
return internal::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t File::write(const void *buffer, std::size_t count) {
|
std::size_t file::write(const void *buffer, std::size_t count) {
|
||||||
RWResult result = 0;
|
RWResult result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
@ -169,16 +169,16 @@ std::size_t File::write(const void *buffer, std::size_t count) {
|
|||||||
return internal::to_unsigned(result);
|
return internal::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
File File::dup(int fd) {
|
file file::dup(int fd) {
|
||||||
// Don't retry as dup doesn't return EINTR.
|
// Don't retry as dup doesn't return EINTR.
|
||||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||||
if (new_fd == -1)
|
if (new_fd == -1)
|
||||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||||
return File(new_fd);
|
return file(new_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void File::dup2(int fd) {
|
void file::dup2(int fd) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
@ -187,14 +187,14 @@ void File::dup2(int fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
|
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
ec = ErrorCode(errno);
|
ec = error_code(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void File::pipe(File &read_end, File &write_end) {
|
void file::pipe(file &read_end, file &write_end) {
|
||||||
// Close the descriptors first to make sure that assignments don't throw
|
// Close the descriptors first to make sure that assignments don't throw
|
||||||
// and there are no leaks.
|
// and there are no leaks.
|
||||||
read_end.close();
|
read_end.close();
|
||||||
@ -213,17 +213,17 @@ void File::pipe(File &read_end, File &write_end) {
|
|||||||
FMT_THROW(system_error(errno, "cannot create pipe"));
|
FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||||
// The following assignments don't throw because read_fd and write_fd
|
// The following assignments don't throw because read_fd and write_fd
|
||||||
// are closed.
|
// are closed.
|
||||||
read_end = File(fds[0]);
|
read_end = file(fds[0]);
|
||||||
write_end = File(fds[1]);
|
write_end = file(fds[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedFile File::fdopen(const char *mode) {
|
buffered_file file::fdopen(const char *mode) {
|
||||||
// Don't retry as fdopen doesn't return EINTR.
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
if (!f)
|
if (!f)
|
||||||
FMT_THROW(system_error(errno,
|
FMT_THROW(system_error(errno,
|
||||||
"cannot associate stream with file descriptor"));
|
"cannot associate stream with file descriptor"));
|
||||||
BufferedFile file(f);
|
buffered_file file(f);
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,8 +9,7 @@ set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
|
|||||||
function (generate_source result fragment)
|
function (generate_source result fragment)
|
||||||
set(${result} "
|
set(${result} "
|
||||||
#define FMT_HEADER_ONLY 1
|
#define FMT_HEADER_ONLY 1
|
||||||
#include \"fmt/posix.h\"
|
#include \"fmt/format.h\"
|
||||||
#include \"fmt/ostream.h\"
|
|
||||||
int main() {
|
int main() {
|
||||||
${fragment}
|
${fragment}
|
||||||
}
|
}
|
||||||
@ -58,6 +57,14 @@ expect_compile_error("fmt::format(\"{}\", L\"foo\");")
|
|||||||
# mixing UTF-8 with UTF-16/32 can result in an invalid output.
|
# mixing UTF-8 with UTF-16/32 can result in an invalid output.
|
||||||
expect_compile_error("fmt::format(L\"{}\", \"foo\");")
|
expect_compile_error("fmt::format(L\"{}\", \"foo\");")
|
||||||
|
|
||||||
|
# Formatting a wide string with a narrow format string is forbidden.
|
||||||
|
expect_compile_error("
|
||||||
|
struct S {
|
||||||
|
operator std::string() const { return std::string(); }
|
||||||
|
};
|
||||||
|
fmt::format(\"{}\", S());
|
||||||
|
")
|
||||||
|
|
||||||
# Make sure that compiler features detected in the header
|
# Make sure that compiler features detected in the header
|
||||||
# match the features detected in CMake.
|
# match the features detected in CMake.
|
||||||
if (SUPPORTS_USER_DEFINED_LITERALS)
|
if (SUPPORTS_USER_DEFINED_LITERALS)
|
||||||
|
|||||||
@ -1072,16 +1072,6 @@ TEST(FormatterTest, FormatStdStringView) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct ConvertibleToString {
|
|
||||||
std::string s;
|
|
||||||
ConvertibleToString() : s("foo") {}
|
|
||||||
operator const std::string &() const { return s; }
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(FormatterTest, FormatConvertibleToString) {
|
|
||||||
EXPECT_EQ("foo", format("{}", ConvertibleToString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ConvertibleToStringView {
|
struct ConvertibleToStringView {
|
||||||
operator fmt::string_view() const { return "foo"; }
|
operator fmt::string_view() const { return "foo"; }
|
||||||
};
|
};
|
||||||
@ -1217,23 +1207,23 @@ TEST(FormatterTest, Examples) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatIntTest, Data) {
|
TEST(FormatIntTest, Data) {
|
||||||
fmt::FormatInt format_int(42);
|
fmt::format_int format_int(42);
|
||||||
EXPECT_EQ("42", std::string(format_int.data(), format_int.size()));
|
EXPECT_EQ("42", std::string(format_int.data(), format_int.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatIntTest, FormatInt) {
|
TEST(FormatIntTest, FormatInt) {
|
||||||
EXPECT_EQ("42", fmt::FormatInt(42).str());
|
EXPECT_EQ("42", fmt::format_int(42).str());
|
||||||
EXPECT_EQ(2u, fmt::FormatInt(42).size());
|
EXPECT_EQ(2u, fmt::format_int(42).size());
|
||||||
EXPECT_EQ("-42", fmt::FormatInt(-42).str());
|
EXPECT_EQ("-42", fmt::format_int(-42).str());
|
||||||
EXPECT_EQ(3u, fmt::FormatInt(-42).size());
|
EXPECT_EQ(3u, fmt::format_int(-42).size());
|
||||||
EXPECT_EQ("42", fmt::FormatInt(42ul).str());
|
EXPECT_EQ("42", fmt::format_int(42ul).str());
|
||||||
EXPECT_EQ("-42", fmt::FormatInt(-42l).str());
|
EXPECT_EQ("-42", fmt::format_int(-42l).str());
|
||||||
EXPECT_EQ("42", fmt::FormatInt(42ull).str());
|
EXPECT_EQ("42", fmt::format_int(42ull).str());
|
||||||
EXPECT_EQ("-42", fmt::FormatInt(-42ll).str());
|
EXPECT_EQ("-42", fmt::format_int(-42ll).str());
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << std::numeric_limits<int64_t>::max();
|
os << std::numeric_limits<int64_t>::max();
|
||||||
EXPECT_EQ(os.str(),
|
EXPECT_EQ(os.str(),
|
||||||
fmt::FormatInt(std::numeric_limits<int64_t>::max()).str());
|
fmt::format_int(std::numeric_limits<int64_t>::max()).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|||||||
@ -306,20 +306,20 @@ TEST(UtilTest, FormatSystemError) {
|
|||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
using fmt::BufferedFile;
|
using fmt::buffered_file;
|
||||||
using fmt::ErrorCode;
|
using fmt::error_code;
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
TEST(ErrorCodeTest, Ctor) {
|
TEST(ErrorCodeTest, Ctor) {
|
||||||
EXPECT_EQ(0, ErrorCode().get());
|
EXPECT_EQ(0, error_code().get());
|
||||||
EXPECT_EQ(42, ErrorCode(42).get());
|
EXPECT_EQ(42, error_code(42).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, ScopedRedirect) {
|
TEST(OutputRedirectTest, ScopedRedirect) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
{
|
{
|
||||||
BufferedFile file(write_end.fdopen("w"));
|
buffered_file file(write_end.fdopen("w"));
|
||||||
std::fprintf(file.get(), "[[[");
|
std::fprintf(file.get(), "[[[");
|
||||||
{
|
{
|
||||||
OutputRedirect redir(file.get());
|
OutputRedirect redir(file.get());
|
||||||
@ -332,11 +332,11 @@ TEST(OutputRedirectTest, ScopedRedirect) {
|
|||||||
|
|
||||||
// Test that OutputRedirect handles errors in flush correctly.
|
// Test that OutputRedirect handles errors in flush correctly.
|
||||||
TEST(OutputRedirectTest, FlushErrorInCtor) {
|
TEST(OutputRedirectTest, FlushErrorInCtor) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
File write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
BufferedFile f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
FMT_POSIX(close(write_fd));
|
FMT_POSIX(close(write_fd));
|
||||||
@ -348,9 +348,9 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, DupErrorInCtor) {
|
TEST(OutputRedirectTest, DupErrorInCtor) {
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int fd = (f.fileno)();
|
int fd = (f.fileno)();
|
||||||
File copy = File::dup(fd);
|
file copy = file::dup(fd);
|
||||||
FMT_POSIX(close(fd));
|
FMT_POSIX(close(fd));
|
||||||
scoped_ptr<OutputRedirect> redir;
|
scoped_ptr<OutputRedirect> redir;
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
|
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
|
||||||
@ -359,26 +359,26 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, RestoreAndRead) {
|
TEST(OutputRedirectTest, RestoreAndRead) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
BufferedFile file(write_end.fdopen("w"));
|
buffered_file file(write_end.fdopen("w"));
|
||||||
std::fprintf(file.get(), "[[[");
|
std::fprintf(file.get(), "[[[");
|
||||||
OutputRedirect redir(file.get());
|
OutputRedirect redir(file.get());
|
||||||
std::fprintf(file.get(), "censored");
|
std::fprintf(file.get(), "censored");
|
||||||
EXPECT_EQ("censored", sanitize(redir.restore_and_read()));
|
EXPECT_EQ("censored", sanitize(redir.restore_and_read()));
|
||||||
EXPECT_EQ("", sanitize(redir.restore_and_read()));
|
EXPECT_EQ("", sanitize(redir.restore_and_read()));
|
||||||
std::fprintf(file.get(), "]]]");
|
std::fprintf(file.get(), "]]]");
|
||||||
file = BufferedFile();
|
file = buffered_file();
|
||||||
EXPECT_READ(read_end, "[[[]]]");
|
EXPECT_READ(read_end, "[[[]]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that OutputRedirect handles errors in flush correctly.
|
// Test that OutputRedirect handles errors in flush correctly.
|
||||||
TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
File write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
BufferedFile f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
OutputRedirect redir(f.get());
|
OutputRedirect redir(f.get());
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
@ -389,11 +389,11 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(OutputRedirectTest, ErrorInDtor) {
|
TEST(OutputRedirectTest, ErrorInDtor) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int write_fd = write_end.descriptor();
|
int write_fd = write_end.descriptor();
|
||||||
File write_copy = write_end.dup(write_fd);
|
file write_copy = write_end.dup(write_fd);
|
||||||
BufferedFile f = write_end.fdopen("w");
|
buffered_file f = write_end.fdopen("w");
|
||||||
scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
|
scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
|
||||||
// Put a character in a file buffer.
|
// Put a character in a file buffer.
|
||||||
EXPECT_EQ('x', fputc('x', f.get()));
|
EXPECT_EQ('x', fputc('x', f.get()));
|
||||||
@ -405,7 +405,7 @@ TEST(OutputRedirectTest, ErrorInDtor) {
|
|||||||
FMT_POSIX(close(write_fd));
|
FMT_POSIX(close(write_fd));
|
||||||
SUPPRESS_ASSERT(redir.reset());
|
SUPPRESS_ASSERT(redir.reset());
|
||||||
}, format_system_error(EBADF, "cannot flush stream"));
|
}, format_system_error(EBADF, "cannot flush stream"));
|
||||||
write_copy.dup2(write_fd); // "undo" close or dtor of BufferedFile will fail
|
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
void OutputRedirect::flush() {
|
void OutputRedirect::flush() {
|
||||||
#if EOF != -1
|
#if EOF != -1
|
||||||
@ -30,14 +30,14 @@ void OutputRedirect::restore() {
|
|||||||
original_.close();
|
original_.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputRedirect::OutputRedirect(FILE *file) : file_(file) {
|
OutputRedirect::OutputRedirect(FILE *f) : file_(f) {
|
||||||
flush();
|
flush();
|
||||||
int fd = FMT_POSIX(fileno(file));
|
int fd = FMT_POSIX(fileno(f));
|
||||||
// Create a File object referring to the original file.
|
// Create a file object referring to the original file.
|
||||||
original_ = File::dup(fd);
|
original_ = file::dup(fd);
|
||||||
// Create a pipe.
|
// Create a pipe.
|
||||||
File write_end;
|
file write_end;
|
||||||
File::pipe(read_end_, write_end);
|
file::pipe(read_end_, write_end);
|
||||||
// Connect the passed FILE object to the write end of the pipe.
|
// Connect the passed FILE object to the write end of the pipe.
|
||||||
write_end.dup2(fd);
|
write_end.dup2(fd);
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ std::string OutputRedirect::restore_and_read() {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string read(File &f, std::size_t count) {
|
std::string read(file &f, std::size_t count) {
|
||||||
std::string buffer(count, '\0');
|
std::string buffer(count, '\0');
|
||||||
std::size_t n = 0, offset = 0;
|
std::size_t n = 0, offset = 0;
|
||||||
do {
|
do {
|
||||||
|
|||||||
@ -74,8 +74,8 @@ std::string format_system_error(int error_code, fmt::string_view message);
|
|||||||
class OutputRedirect {
|
class OutputRedirect {
|
||||||
private:
|
private:
|
||||||
FILE *file_;
|
FILE *file_;
|
||||||
fmt::File original_; // Original file passed to redirector.
|
fmt::file original_; // Original file passed to redirector.
|
||||||
fmt::File read_end_; // Read end of the pipe where the output is redirected.
|
fmt::file read_end_; // Read end of the pipe where the output is redirected.
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ class SuppressAssert {
|
|||||||
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
|
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
|
||||||
|
|
||||||
// Attempts to read count characters from a file.
|
// Attempts to read count characters from a file.
|
||||||
std::string read(fmt::File &f, std::size_t count);
|
std::string read(fmt::file &f, std::size_t count);
|
||||||
|
|
||||||
#define EXPECT_READ(file, expected_content) \
|
#define EXPECT_READ(file, expected_content) \
|
||||||
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
|
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
|
||||||
|
|||||||
@ -25,9 +25,9 @@
|
|||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::BufferedFile;
|
using fmt::buffered_file;
|
||||||
using fmt::ErrorCode;
|
using fmt::error_code;
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
using testing::internal::scoped_ptr;
|
using testing::internal::scoped_ptr;
|
||||||
using testing::_;
|
using testing::_;
|
||||||
@ -194,7 +194,7 @@ int (test::fileno)(FILE *stream) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||||
fmt::BufferedFile f(filename, "w");
|
fmt::buffered_file f(filename, "w");
|
||||||
f.print("{}", content);
|
f.print("{}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +214,8 @@ TEST(UtilTest, GetPageSize) {
|
|||||||
|
|
||||||
TEST(FileTest, OpenRetry) {
|
TEST(FileTest, OpenRetry) {
|
||||||
write_file("test", "there must be something here");
|
write_file("test", "there must be something here");
|
||||||
scoped_ptr<File> f;
|
scoped_ptr<file> f;
|
||||||
EXPECT_RETRY(f.reset(new File("test", File::RDONLY)),
|
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)),
|
||||||
open, "cannot open file test");
|
open, "cannot open file test");
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
char c = 0;
|
char c = 0;
|
||||||
@ -224,9 +224,9 @@ TEST(FileTest, OpenRetry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseNoRetryInDtor) {
|
TEST(FileTest, CloseNoRetryInDtor) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
scoped_ptr<File> f(new File(std::move(read_end)));
|
scoped_ptr<file> f(new file(std::move(read_end)));
|
||||||
int saved_close_count = 0;
|
int saved_close_count = 0;
|
||||||
EXPECT_WRITE(stderr, {
|
EXPECT_WRITE(stderr, {
|
||||||
close_count = 1;
|
close_count = 1;
|
||||||
@ -238,8 +238,8 @@ TEST(FileTest, CloseNoRetryInDtor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseNoRetry) {
|
TEST(FileTest, CloseNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
close_count = 1;
|
close_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
|
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
|
||||||
EXPECT_EQ(2, close_count);
|
EXPECT_EQ(2, close_count);
|
||||||
@ -249,7 +249,7 @@ TEST(FileTest, CloseNoRetry) {
|
|||||||
TEST(FileTest, Size) {
|
TEST(FileTest, Size) {
|
||||||
std::string content = "top secret, destroy before reading";
|
std::string content = "top secret, destroy before reading";
|
||||||
write_file("test", content);
|
write_file("test", content);
|
||||||
File f("test", File::RDONLY);
|
file f("test", file::RDONLY);
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -267,7 +267,7 @@ TEST(FileTest, Size) {
|
|||||||
|
|
||||||
TEST(FileTest, MaxSize) {
|
TEST(FileTest, MaxSize) {
|
||||||
write_file("test", "");
|
write_file("test", "");
|
||||||
File f("test", File::RDONLY);
|
file f("test", file::RDONLY);
|
||||||
fstat_sim = MAX_SIZE;
|
fstat_sim = MAX_SIZE;
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(max_file_size(), f.size());
|
EXPECT_EQ(max_file_size(), f.size());
|
||||||
@ -275,8 +275,8 @@ TEST(FileTest, MaxSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ReadRetry) {
|
TEST(FileTest, ReadRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
enum { SIZE = 4 };
|
enum { SIZE = 4 };
|
||||||
write_end.write("test", SIZE);
|
write_end.write("test", SIZE);
|
||||||
write_end.close();
|
write_end.close();
|
||||||
@ -288,8 +288,8 @@ TEST(FileTest, ReadRetry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, WriteRetry) {
|
TEST(FileTest, WriteRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
enum { SIZE = 4 };
|
enum { SIZE = 4 };
|
||||||
std::size_t count = 0;
|
std::size_t count = 0;
|
||||||
EXPECT_RETRY(count = write_end.write("test", SIZE),
|
EXPECT_RETRY(count = write_end.write("test", SIZE),
|
||||||
@ -306,8 +306,8 @@ TEST(FileTest, WriteRetry) {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
TEST(FileTest, ConvertReadCount) {
|
TEST(FileTest, ConvertReadCount) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
char c;
|
char c;
|
||||||
std::size_t size = UINT_MAX;
|
std::size_t size = UINT_MAX;
|
||||||
if (sizeof(unsigned) != sizeof(std::size_t))
|
if (sizeof(unsigned) != sizeof(std::size_t))
|
||||||
@ -320,8 +320,8 @@ TEST(FileTest, ConvertReadCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ConvertWriteCount) {
|
TEST(FileTest, ConvertWriteCount) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
char c;
|
char c;
|
||||||
std::size_t size = UINT_MAX;
|
std::size_t size = UINT_MAX;
|
||||||
if (sizeof(unsigned) != sizeof(std::size_t))
|
if (sizeof(unsigned) != sizeof(std::size_t))
|
||||||
@ -337,14 +337,14 @@ TEST(FileTest, ConvertWriteCount) {
|
|||||||
TEST(FileTest, DupNoRetry) {
|
TEST(FileTest, DupNoRetry) {
|
||||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||||
dup_count = 1;
|
dup_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR,
|
EXPECT_SYSTEM_ERROR(file::dup(stdout_fd), EINTR,
|
||||||
fmt::format("cannot duplicate file descriptor {}", stdout_fd));
|
fmt::format("cannot duplicate file descriptor {}", stdout_fd));
|
||||||
dup_count = 0;
|
dup_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2Retry) {
|
TEST(FileTest, Dup2Retry) {
|
||||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||||
File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd);
|
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
||||||
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
|
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
|
||||||
fmt::format("cannot duplicate file descriptor {} to {}",
|
fmt::format("cannot duplicate file descriptor {} to {}",
|
||||||
f1.descriptor(), f2.descriptor()));
|
f1.descriptor(), f2.descriptor()));
|
||||||
@ -352,8 +352,8 @@ TEST(FileTest, Dup2Retry) {
|
|||||||
|
|
||||||
TEST(FileTest, Dup2NoExceptRetry) {
|
TEST(FileTest, Dup2NoExceptRetry) {
|
||||||
int stdout_fd = FMT_POSIX(fileno(stdout));
|
int stdout_fd = FMT_POSIX(fileno(stdout));
|
||||||
File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd);
|
file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
|
||||||
ErrorCode ec;
|
error_code ec;
|
||||||
dup2_count = 1;
|
dup2_count = 1;
|
||||||
f1.dup2(f2.descriptor(), ec);
|
f1.dup2(f2.descriptor(), ec);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@ -365,16 +365,16 @@ TEST(FileTest, Dup2NoExceptRetry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, PipeNoRetry) {
|
TEST(FileTest, PipeNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
pipe_count = 1;
|
pipe_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(
|
EXPECT_SYSTEM_ERROR(
|
||||||
File::pipe(read_end, write_end), EINTR, "cannot create pipe");
|
file::pipe(read_end, write_end), EINTR, "cannot create pipe");
|
||||||
pipe_count = 0;
|
pipe_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, FdopenNoRetry) {
|
TEST(FileTest, FdopenNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
fdopen_count = 1;
|
fdopen_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
|
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
|
||||||
EINTR, "cannot associate stream with file descriptor");
|
EINTR, "cannot associate stream with file descriptor");
|
||||||
@ -383,8 +383,8 @@ TEST(FileTest, FdopenNoRetry) {
|
|||||||
|
|
||||||
TEST(BufferedFileTest, OpenRetry) {
|
TEST(BufferedFileTest, OpenRetry) {
|
||||||
write_file("test", "there must be something here");
|
write_file("test", "there must be something here");
|
||||||
scoped_ptr<BufferedFile> f;
|
scoped_ptr<buffered_file> f;
|
||||||
EXPECT_RETRY(f.reset(new BufferedFile("test", "r")),
|
EXPECT_RETRY(f.reset(new buffered_file("test", "r")),
|
||||||
fopen, "cannot open file test");
|
fopen, "cannot open file test");
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
char c = 0;
|
char c = 0;
|
||||||
@ -394,9 +394,9 @@ TEST(BufferedFileTest, OpenRetry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
scoped_ptr<BufferedFile> f(new BufferedFile(read_end.fdopen("r")));
|
scoped_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
||||||
int saved_fclose_count = 0;
|
int saved_fclose_count = 0;
|
||||||
EXPECT_WRITE(stderr, {
|
EXPECT_WRITE(stderr, {
|
||||||
fclose_count = 1;
|
fclose_count = 1;
|
||||||
@ -408,9 +408,9 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseNoRetry) {
|
TEST(BufferedFileTest, CloseNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
BufferedFile f = read_end.fdopen("r");
|
buffered_file f = read_end.fdopen("r");
|
||||||
fclose_count = 1;
|
fclose_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
|
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
|
||||||
EXPECT_EQ(2, fclose_count);
|
EXPECT_EQ(2, fclose_count);
|
||||||
@ -418,9 +418,9 @@ TEST(BufferedFileTest, CloseNoRetry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, FilenoNoRetry) {
|
TEST(BufferedFileTest, FilenoNoRetry) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
BufferedFile f = read_end.fdopen("r");
|
buffered_file f = read_end.fdopen("r");
|
||||||
fileno_count = 1;
|
fileno_count = 1;
|
||||||
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
|
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
|
||||||
EXPECT_EQ(2, fileno_count);
|
EXPECT_EQ(2, fileno_count);
|
||||||
|
|||||||
@ -16,9 +16,9 @@
|
|||||||
# undef fileno
|
# undef fileno
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using fmt::BufferedFile;
|
using fmt::buffered_file;
|
||||||
using fmt::ErrorCode;
|
using fmt::error_code;
|
||||||
using fmt::File;
|
using fmt::file;
|
||||||
|
|
||||||
using testing::internal::scoped_ptr;
|
using testing::internal::scoped_ptr;
|
||||||
|
|
||||||
@ -36,16 +36,16 @@ bool isclosed(int fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Opens a file for reading.
|
// Opens a file for reading.
|
||||||
File open_file() {
|
file open_file() {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
||||||
write_end.close();
|
write_end.close();
|
||||||
return read_end;
|
return read_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to write a string to a file.
|
// Attempts to write a string to a file.
|
||||||
void write(File &f, fmt::string_view s) {
|
void write(file &f, fmt::string_view s) {
|
||||||
std::size_t num_chars_left = s.size();
|
std::size_t num_chars_left = s.size();
|
||||||
const char *ptr = s.data();
|
const char *ptr = s.data();
|
||||||
do {
|
do {
|
||||||
@ -58,32 +58,32 @@ void write(File &f, fmt::string_view s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, DefaultCtor) {
|
TEST(BufferedFileTest, DefaultCtor) {
|
||||||
BufferedFile f;
|
buffered_file f;
|
||||||
EXPECT_TRUE(f.get() == 0);
|
EXPECT_TRUE(f.get() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveCtor) {
|
TEST(BufferedFileTest, MoveCtor) {
|
||||||
BufferedFile bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
FILE *fp = bf.get();
|
FILE *fp = bf.get();
|
||||||
EXPECT_TRUE(fp != 0);
|
EXPECT_TRUE(fp != 0);
|
||||||
BufferedFile bf2(std::move(bf));
|
buffered_file bf2(std::move(bf));
|
||||||
EXPECT_EQ(fp, bf2.get());
|
EXPECT_EQ(fp, bf2.get());
|
||||||
EXPECT_TRUE(bf.get() == 0);
|
EXPECT_TRUE(bf.get() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveAssignment) {
|
TEST(BufferedFileTest, MoveAssignment) {
|
||||||
BufferedFile bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
FILE *fp = bf.get();
|
FILE *fp = bf.get();
|
||||||
EXPECT_TRUE(fp != 0);
|
EXPECT_TRUE(fp != 0);
|
||||||
BufferedFile bf2;
|
buffered_file bf2;
|
||||||
bf2 = std::move(bf);
|
bf2 = std::move(bf);
|
||||||
EXPECT_EQ(fp, bf2.get());
|
EXPECT_EQ(fp, bf2.get());
|
||||||
EXPECT_TRUE(bf.get() == 0);
|
EXPECT_TRUE(bf.get() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
||||||
BufferedFile bf = open_buffered_file();
|
buffered_file bf = open_buffered_file();
|
||||||
BufferedFile bf2 = open_buffered_file();
|
buffered_file bf2 = open_buffered_file();
|
||||||
int old_fd = bf2.fileno();
|
int old_fd = bf2.fileno();
|
||||||
bf2 = std::move(bf);
|
bf2 = std::move(bf);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
@ -91,19 +91,19 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
|
|||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
|
||||||
FILE *fp = 0;
|
FILE *fp = 0;
|
||||||
BufferedFile f(open_buffered_file(&fp));
|
buffered_file f(open_buffered_file(&fp));
|
||||||
EXPECT_EQ(fp, f.get());
|
EXPECT_EQ(fp, f.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
|
||||||
FILE *fp = 0;
|
FILE *fp = 0;
|
||||||
BufferedFile f;
|
buffered_file f;
|
||||||
f = open_buffered_file(&fp);
|
f = open_buffered_file(&fp);
|
||||||
EXPECT_EQ(fp, f.get());
|
EXPECT_EQ(fp, f.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int old_fd = f.fileno();
|
int old_fd = f.fileno();
|
||||||
f = open_buffered_file();
|
f = open_buffered_file();
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
@ -112,14 +112,14 @@ TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
|||||||
TEST(BufferedFileTest, CloseFileInDtor) {
|
TEST(BufferedFileTest, CloseFileInDtor) {
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
{
|
{
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
fd = f.fileno();
|
fd = f.fileno();
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseErrorInDtor) {
|
TEST(BufferedFileTest, CloseErrorInDtor) {
|
||||||
scoped_ptr<BufferedFile> f(new BufferedFile(open_buffered_file()));
|
scoped_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
|
||||||
EXPECT_WRITE(stderr, {
|
EXPECT_WRITE(stderr, {
|
||||||
// The close function must be called inside EXPECT_WRITE, otherwise
|
// The close function must be called inside EXPECT_WRITE, otherwise
|
||||||
// the system may recycle closed file descriptor when redirecting the
|
// the system may recycle closed file descriptor when redirecting the
|
||||||
@ -131,7 +131,7 @@ TEST(BufferedFileTest, CloseErrorInDtor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, Close) {
|
TEST(BufferedFileTest, Close) {
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
int fd = f.fileno();
|
int fd = f.fileno();
|
||||||
f.close();
|
f.close();
|
||||||
EXPECT_TRUE(f.get() == 0);
|
EXPECT_TRUE(f.get() == 0);
|
||||||
@ -139,14 +139,14 @@ TEST(BufferedFileTest, Close) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, CloseError) {
|
TEST(BufferedFileTest, CloseError) {
|
||||||
BufferedFile f = open_buffered_file();
|
buffered_file f = open_buffered_file();
|
||||||
FMT_POSIX(close(f.fileno()));
|
FMT_POSIX(close(f.fileno()));
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||||
EXPECT_TRUE(f.get() == 0);
|
EXPECT_TRUE(f.get() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, Fileno) {
|
TEST(BufferedFileTest, Fileno) {
|
||||||
BufferedFile f;
|
buffered_file f;
|
||||||
#ifndef __COVERITY__
|
#ifndef __COVERITY__
|
||||||
// fileno on a null FILE pointer either crashes or returns an error.
|
// fileno on a null FILE pointer either crashes or returns an error.
|
||||||
// Disable Coverity because this is intentional.
|
// Disable Coverity because this is intentional.
|
||||||
@ -160,12 +160,12 @@ TEST(BufferedFileTest, Fileno) {
|
|||||||
#endif
|
#endif
|
||||||
f = open_buffered_file();
|
f = open_buffered_file();
|
||||||
EXPECT_TRUE(f.fileno() != -1);
|
EXPECT_TRUE(f.fileno() != -1);
|
||||||
File copy = File::dup(f.fileno());
|
file copy = file::dup(f.fileno());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, FILE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, DefaultCtor) {
|
TEST(FileTest, DefaultCtor) {
|
||||||
File f;
|
file f;
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,64 +173,64 @@ TEST(FileTest, OpenBufferedFileInCtor) {
|
|||||||
FILE *fp = safe_fopen("test-file", "w");
|
FILE *fp = safe_fopen("test-file", "w");
|
||||||
std::fputs(FILE_CONTENT, fp);
|
std::fputs(FILE_CONTENT, fp);
|
||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
File f("test-file", File::RDONLY);
|
file f("test-file", file::RDONLY);
|
||||||
ASSERT_TRUE(isopen(f.descriptor()));
|
ASSERT_TRUE(isopen(f.descriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, OpenBufferedFileError) {
|
TEST(FileTest, OpenBufferedFileError) {
|
||||||
EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
|
EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY),
|
||||||
ENOENT, "cannot open file nonexistent");
|
ENOENT, "cannot open file nonexistent");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveCtor) {
|
TEST(FileTest, MoveCtor) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
EXPECT_NE(-1, fd);
|
EXPECT_NE(-1, fd);
|
||||||
File f2(std::move(f));
|
file f2(std::move(f));
|
||||||
EXPECT_EQ(fd, f2.descriptor());
|
EXPECT_EQ(fd, f2.descriptor());
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveAssignment) {
|
TEST(FileTest, MoveAssignment) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
EXPECT_NE(-1, fd);
|
EXPECT_NE(-1, fd);
|
||||||
File f2;
|
file f2;
|
||||||
f2 = std::move(f);
|
f2 = std::move(f);
|
||||||
EXPECT_EQ(fd, f2.descriptor());
|
EXPECT_EQ(fd, f2.descriptor());
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveAssignmentClosesFile) {
|
TEST(FileTest, MoveAssignmentClosesFile) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File f2 = open_file();
|
file f2 = open_file();
|
||||||
int old_fd = f2.descriptor();
|
int old_fd = f2.descriptor();
|
||||||
f2 = std::move(f);
|
f2 = std::move(f);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
File OpenBufferedFile(int &fd) {
|
file OpenBufferedFile(int &fd) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
fd = f.descriptor();
|
fd = f.descriptor();
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInCtor) {
|
TEST(FileTest, MoveFromTemporaryInCtor) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
File f(OpenBufferedFile(fd));
|
file f(OpenBufferedFile(fd));
|
||||||
EXPECT_EQ(fd, f.descriptor());
|
EXPECT_EQ(fd, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInAssignment) {
|
TEST(FileTest, MoveFromTemporaryInAssignment) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
File f;
|
file f;
|
||||||
f = OpenBufferedFile(fd);
|
f = OpenBufferedFile(fd);
|
||||||
EXPECT_EQ(fd, f.descriptor());
|
EXPECT_EQ(fd, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
||||||
int fd = 0xdead;
|
int fd = 0xdead;
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int old_fd = f.descriptor();
|
int old_fd = f.descriptor();
|
||||||
f = OpenBufferedFile(fd);
|
f = OpenBufferedFile(fd);
|
||||||
EXPECT_TRUE(isclosed(old_fd));
|
EXPECT_TRUE(isclosed(old_fd));
|
||||||
@ -239,14 +239,14 @@ TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
|
|||||||
TEST(FileTest, CloseFileInDtor) {
|
TEST(FileTest, CloseFileInDtor) {
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
{
|
{
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
fd = f.descriptor();
|
fd = f.descriptor();
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(isclosed(fd));
|
EXPECT_TRUE(isclosed(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseErrorInDtor) {
|
TEST(FileTest, CloseErrorInDtor) {
|
||||||
scoped_ptr<File> f(new File(open_file()));
|
scoped_ptr<file> f(new file(open_file()));
|
||||||
EXPECT_WRITE(stderr, {
|
EXPECT_WRITE(stderr, {
|
||||||
// The close function must be called inside EXPECT_WRITE, otherwise
|
// The close function must be called inside EXPECT_WRITE, otherwise
|
||||||
// the system may recycle closed file descriptor when redirecting the
|
// the system may recycle closed file descriptor when redirecting the
|
||||||
@ -258,7 +258,7 @@ TEST(FileTest, CloseErrorInDtor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Close) {
|
TEST(FileTest, Close) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
int fd = f.descriptor();
|
int fd = f.descriptor();
|
||||||
f.close();
|
f.close();
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
@ -266,19 +266,19 @@ TEST(FileTest, Close) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, CloseError) {
|
TEST(FileTest, CloseError) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
FMT_POSIX(close(f.descriptor()));
|
FMT_POSIX(close(f.descriptor()));
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||||
EXPECT_EQ(-1, f.descriptor());
|
EXPECT_EQ(-1, f.descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Read) {
|
TEST(FileTest, Read) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
EXPECT_READ(f, FILE_CONTENT);
|
EXPECT_READ(f, FILE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, ReadError) {
|
TEST(FileTest, ReadError) {
|
||||||
File f("test-file", File::WRONLY);
|
file f("test-file", file::WRONLY);
|
||||||
char buf;
|
char buf;
|
||||||
// We intentionally read from a file opened in the write-only mode to
|
// We intentionally read from a file opened in the write-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
@ -286,23 +286,23 @@ TEST(FileTest, ReadError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Write) {
|
TEST(FileTest, Write) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
write(write_end, "test");
|
write(write_end, "test");
|
||||||
write_end.close();
|
write_end.close();
|
||||||
EXPECT_READ(read_end, "test");
|
EXPECT_READ(read_end, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, WriteError) {
|
TEST(FileTest, WriteError) {
|
||||||
File f("test-file", File::RDONLY);
|
file f("test-file", file::RDONLY);
|
||||||
// We intentionally write to a file opened in the read-only mode to
|
// We intentionally write to a file opened in the read-only mode to
|
||||||
// cause error.
|
// cause error.
|
||||||
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup) {
|
TEST(FileTest, Dup) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File copy = File::dup(f.descriptor());
|
file copy = file::dup(f.descriptor());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
|
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
|
||||||
}
|
}
|
||||||
@ -310,29 +310,29 @@ TEST(FileTest, Dup) {
|
|||||||
#ifndef __COVERITY__
|
#ifndef __COVERITY__
|
||||||
TEST(FileTest, DupError) {
|
TEST(FileTest, DupError) {
|
||||||
int value = -1;
|
int value = -1;
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(File::dup(value),
|
EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value),
|
||||||
EBADF, "cannot duplicate file descriptor -1");
|
EBADF, "cannot duplicate file descriptor -1");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(FileTest, Dup2) {
|
TEST(FileTest, Dup2) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File copy = open_file();
|
file copy = open_file();
|
||||||
f.dup2(copy.descriptor());
|
f.dup2(copy.descriptor());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
EXPECT_READ(copy, FILE_CONTENT);
|
EXPECT_READ(copy, FILE_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2Error) {
|
TEST(FileTest, Dup2Error) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
|
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
|
||||||
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExcept) {
|
TEST(FileTest, Dup2NoExcept) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
File copy = open_file();
|
file copy = open_file();
|
||||||
ErrorCode ec;
|
error_code ec;
|
||||||
f.dup2(copy.descriptor(), ec);
|
f.dup2(copy.descriptor(), ec);
|
||||||
EXPECT_EQ(0, ec.get());
|
EXPECT_EQ(0, ec.get());
|
||||||
EXPECT_NE(f.descriptor(), copy.descriptor());
|
EXPECT_NE(f.descriptor(), copy.descriptor());
|
||||||
@ -340,15 +340,15 @@ TEST(FileTest, Dup2NoExcept) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Dup2NoExceptError) {
|
TEST(FileTest, Dup2NoExceptError) {
|
||||||
File f = open_file();
|
file f = open_file();
|
||||||
ErrorCode ec;
|
error_code ec;
|
||||||
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
SUPPRESS_ASSERT(f.dup2(-1, ec));
|
||||||
EXPECT_EQ(EBADF, ec.get());
|
EXPECT_EQ(EBADF, ec.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Pipe) {
|
TEST(FileTest, Pipe) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
EXPECT_NE(-1, read_end.descriptor());
|
EXPECT_NE(-1, read_end.descriptor());
|
||||||
EXPECT_NE(-1, write_end.descriptor());
|
EXPECT_NE(-1, write_end.descriptor());
|
||||||
write(write_end, "test");
|
write(write_end, "test");
|
||||||
@ -356,14 +356,14 @@ TEST(FileTest, Pipe) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, Fdopen) {
|
TEST(FileTest, Fdopen) {
|
||||||
File read_end, write_end;
|
file read_end, write_end;
|
||||||
File::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
int read_fd = read_end.descriptor();
|
int read_fd = read_end.descriptor();
|
||||||
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
|
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, FdopenError) {
|
TEST(FileTest, FdopenError) {
|
||||||
File f;
|
file f;
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||||
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
|
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -478,8 +478,8 @@ TEST(PrintfTest, Examples) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PrintfError) {
|
TEST(PrintfTest, PrintfError) {
|
||||||
fmt::File read_end, write_end;
|
fmt::file read_end, write_end;
|
||||||
fmt::File::pipe(read_end, write_end);
|
fmt::file::pipe(read_end, write_end);
|
||||||
int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
|
int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
|
||||||
EXPECT_LT(result, 0);
|
EXPECT_LT(result, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -402,7 +402,7 @@ TEST(UtilTest, BitCast) {
|
|||||||
uint32_t u[2];
|
uint32_t u[2];
|
||||||
};
|
};
|
||||||
auto s = fmt::internal::bit_cast<S>(uint64_t(42));
|
auto s = fmt::internal::bit_cast<S>(uint64_t(42));
|
||||||
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42);
|
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42u);
|
||||||
s = fmt::internal::bit_cast<S>(uint64_t(~0ull));
|
s = fmt::internal::bit_cast<S>(uint64_t(~0ull));
|
||||||
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull);
|
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,12 +32,12 @@ std::string get_system_error(int error_code) {
|
|||||||
|
|
||||||
const char *const FILE_CONTENT = "Don't panic!";
|
const char *const FILE_CONTENT = "Don't panic!";
|
||||||
|
|
||||||
fmt::BufferedFile open_buffered_file(FILE **fp) {
|
fmt::buffered_file open_buffered_file(FILE **fp) {
|
||||||
fmt::File read_end, write_end;
|
fmt::file read_end, write_end;
|
||||||
fmt::File::pipe(read_end, write_end);
|
fmt::file::pipe(read_end, write_end);
|
||||||
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
|
||||||
write_end.close();
|
write_end.close();
|
||||||
fmt::BufferedFile f = read_end.fdopen("r");
|
fmt::buffered_file f = read_end.fdopen("r");
|
||||||
if (fp)
|
if (fp)
|
||||||
*fp = f.get();
|
*fp = f.get();
|
||||||
return f;
|
return f;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ std::string get_system_error(int error_code);
|
|||||||
extern const char *const FILE_CONTENT;
|
extern const char *const FILE_CONTENT;
|
||||||
|
|
||||||
// Opens a buffered file for reading.
|
// Opens a buffered file for reading.
|
||||||
fmt::BufferedFile open_buffered_file(FILE **fp = 0);
|
fmt::buffered_file open_buffered_file(FILE **fp = 0);
|
||||||
|
|
||||||
inline FILE *safe_fopen(const char *filename, const char *mode) {
|
inline FILE *safe_fopen(const char *filename, const char *mode) {
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user