aboutsummaryrefslogtreecommitdiffstats
path: root/utils/format
diff options
context:
space:
mode:
authorBrooks Davis <brooks@FreeBSD.org>2020-03-17 16:56:50 +0000
committerBrooks Davis <brooks@FreeBSD.org>2020-03-17 16:56:50 +0000
commit08334c51dbb99d9ecd2bb86a2d94ed06da9e167a (patch)
treec43eb24d59bd5c963583a5190caef80fc8387322 /utils/format
downloadsrc-vendor/kyua.tar.gz
src-vendor/kyua.zip
Import the kyua testing framework for infrastructure softwarevendor/kyua/0.13-a685f91vendor/kyua
Imported at 0.13 plus assumulated changes to git hash a685f91. Obtained from: https://github.com/jmmv/kyua Sponsored by: DARPA
Notes
Notes: svn path=/vendor/kyua/dist/; revision=359042 svn path=/vendor/kyua/0.13-a685f91/; revision=359043; tag=vendor/kyua/0.13-a685f91
Diffstat (limited to 'utils/format')
-rw-r--r--utils/format/Kyuafile7
-rw-r--r--utils/format/Makefile.am.inc59
-rw-r--r--utils/format/containers.hpp66
-rw-r--r--utils/format/containers.ipp138
-rw-r--r--utils/format/containers_test.cpp190
-rw-r--r--utils/format/exceptions.cpp110
-rw-r--r--utils/format/exceptions.hpp84
-rw-r--r--utils/format/exceptions_test.cpp74
-rw-r--r--utils/format/formatter.cpp293
-rw-r--r--utils/format/formatter.hpp123
-rw-r--r--utils/format/formatter.ipp76
-rw-r--r--utils/format/formatter_fwd.hpp45
-rw-r--r--utils/format/formatter_test.cpp265
-rw-r--r--utils/format/macros.hpp58
14 files changed, 1588 insertions, 0 deletions
diff --git a/utils/format/Kyuafile b/utils/format/Kyuafile
new file mode 100644
index 000000000000..344ae455422c
--- /dev/null
+++ b/utils/format/Kyuafile
@@ -0,0 +1,7 @@
+syntax(2)
+
+test_suite("kyua")
+
+atf_test_program{name="containers_test"}
+atf_test_program{name="exceptions_test"}
+atf_test_program{name="formatter_test"}
diff --git a/utils/format/Makefile.am.inc b/utils/format/Makefile.am.inc
new file mode 100644
index 000000000000..a37fc4057079
--- /dev/null
+++ b/utils/format/Makefile.am.inc
@@ -0,0 +1,59 @@
+# Copyright 2010 The Kyua Authors.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+libutils_a_SOURCES += utils/format/containers.hpp
+libutils_a_SOURCES += utils/format/containers.ipp
+libutils_a_SOURCES += utils/format/exceptions.cpp
+libutils_a_SOURCES += utils/format/exceptions.hpp
+libutils_a_SOURCES += utils/format/formatter.cpp
+libutils_a_SOURCES += utils/format/formatter.hpp
+libutils_a_SOURCES += utils/format/formatter_fwd.hpp
+libutils_a_SOURCES += utils/format/formatter.ipp
+libutils_a_SOURCES += utils/format/macros.hpp
+
+if WITH_ATF
+tests_utils_formatdir = $(pkgtestsdir)/utils/format
+
+tests_utils_format_DATA = utils/format/Kyuafile
+EXTRA_DIST += $(tests_utils_format_DATA)
+
+tests_utils_format_PROGRAMS = utils/format/containers_test
+utils_format_containers_test_SOURCES = utils/format/containers_test.cpp
+utils_format_containers_test_CXXFLAGS = $(UTILS_CFLAGS) $(ATF_CXX_CFLAGS)
+utils_format_containers_test_LDADD = $(UTILS_LIBS) $(ATF_CXX_LIBS)
+
+tests_utils_format_PROGRAMS += utils/format/exceptions_test
+utils_format_exceptions_test_SOURCES = utils/format/exceptions_test.cpp
+utils_format_exceptions_test_CXXFLAGS = $(UTILS_CFLAGS) $(ATF_CXX_CFLAGS)
+utils_format_exceptions_test_LDADD = $(UTILS_LIBS) $(ATF_CXX_LIBS)
+
+tests_utils_format_PROGRAMS += utils/format/formatter_test
+utils_format_formatter_test_SOURCES = utils/format/formatter_test.cpp
+utils_format_formatter_test_CXXFLAGS = $(UTILS_CFLAGS) $(ATF_CXX_CFLAGS)
+utils_format_formatter_test_LDADD = $(UTILS_LIBS) $(ATF_CXX_LIBS)
+endif
diff --git a/utils/format/containers.hpp b/utils/format/containers.hpp
new file mode 100644
index 000000000000..7334c250de4e
--- /dev/null
+++ b/utils/format/containers.hpp
@@ -0,0 +1,66 @@
+// Copyright 2014 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file utils/format/containers.hpp
+/// Overloads to support formatting various base container types.
+
+#if !defined(UTILS_FORMAT_CONTAINERS_HPP)
+#define UTILS_FORMAT_CONTAINERS_HPP
+
+#include <map>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <utility>
+#include <vector>
+
+
+// This is ugly but necessary for C++ name resolution. Unsure if we'd do it
+// differently...
+namespace std {
+
+
+template< typename K, typename V >
+std::ostream& operator<<(std::ostream&, const std::map< K, V >&);
+
+template< typename T1, typename T2 >
+std::ostream& operator<<(std::ostream&, const std::pair< T1, T2 >&);
+
+template< typename T >
+std::ostream& operator<<(std::ostream&, const std::shared_ptr< T >);
+
+template< typename T >
+std::ostream& operator<<(std::ostream&, const std::set< T >&);
+
+template< typename T >
+std::ostream& operator<<(std::ostream&, const std::vector< T >&);
+
+
+} // namespace std
+
+#endif // !defined(UTILS_FORMAT_CONTAINERS_HPP)
diff --git a/utils/format/containers.ipp b/utils/format/containers.ipp
new file mode 100644
index 000000000000..11d8e2914149
--- /dev/null
+++ b/utils/format/containers.ipp
@@ -0,0 +1,138 @@
+// Copyright 2014 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#if !defined(UTILS_FORMAT_CONTAINERS_IPP)
+#define UTILS_FORMAT_CONTAINERS_IPP
+
+#include "utils/format/containers.hpp"
+
+#include <ostream>
+
+
+/// Injects the object into a stream.
+///
+/// \param output The stream into which to inject the object.
+/// \param object The object to format.
+///
+/// \return The output stream.
+template< typename K, typename V >
+std::ostream&
+std::operator<<(std::ostream& output, const std::map< K, V >& object)
+{
+ output << "map(";
+ typename std::map< K, V >::size_type counter = 0;
+ for (typename std::map< K, V >::const_iterator iter = object.begin();
+ iter != object.end(); ++iter, ++counter) {
+ if (counter != 0)
+ output << ", ";
+ output << (*iter).first << "=" << (*iter).second;
+ }
+ output << ")";
+ return output;
+}
+
+
+/// Injects the object into a stream.
+///
+/// \param output The stream into which to inject the object.
+/// \param object The object to format.
+///
+/// \return The output stream.
+template< typename T1, typename T2 >
+std::ostream&
+std::operator<<(std::ostream& output, const std::pair< T1, T2 >& object)
+{
+ output << "pair(" << object.first << ", " << object.second << ")";
+ return output;
+}
+
+
+/// Injects the object into a stream.
+///
+/// \param output The stream into which to inject the object.
+/// \param object The object to format.
+///
+/// \return The output stream.
+template< typename T >
+std::ostream&
+std::operator<<(std::ostream& output, const std::shared_ptr< T > object)
+{
+ if (object.get() == NULL) {
+ output << "<NULL>";
+ } else {
+ output << *object;
+ }
+ return output;
+}
+
+
+/// Injects the object into a stream.
+///
+/// \param output The stream into which to inject the object.
+/// \param object The object to format.
+///
+/// \return The output stream.
+template< typename T >
+std::ostream&
+std::operator<<(std::ostream& output, const std::set< T >& object)
+{
+ output << "set(";
+ typename std::set< T >::size_type counter = 0;
+ for (typename std::set< T >::const_iterator iter = object.begin();
+ iter != object.end(); ++iter, ++counter) {
+ if (counter != 0)
+ output << ", ";
+ output << (*iter);
+ }
+ output << ")";
+ return output;
+}
+
+
+/// Injects the object into a stream.
+///
+/// \param output The stream into which to inject the object.
+/// \param object The object to format.
+///
+/// \return The output stream.
+template< typename T >
+std::ostream&
+std::operator<<(std::ostream& output, const std::vector< T >& object)
+{
+ output << "[";
+ for (typename std::vector< T >::size_type i = 0; i < object.size(); ++i) {
+ if (i != 0)
+ output << ", ";
+ output << object[i];
+ }
+ output << "]";
+ return output;
+}
+
+
+#endif // !defined(UTILS_FORMAT_CONTAINERS_IPP)
diff --git a/utils/format/containers_test.cpp b/utils/format/containers_test.cpp
new file mode 100644
index 000000000000..e1c452da2df6
--- /dev/null
+++ b/utils/format/containers_test.cpp
@@ -0,0 +1,190 @@
+// Copyright 2014 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "utils/format/containers.ipp"
+
+#include <memory>
+#include <ostream>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <atf-c++.hpp>
+
+
+
+namespace {
+
+
+/// Formats a value and compares it to an expected string.
+///
+/// \tparam T The type of the value to format.
+/// \param expected Expected formatted text.
+/// \param actual The value to format.
+///
+/// \post Fails the test case if the formatted actual value does not match
+/// the provided expected string.
+template< typename T >
+static void
+do_check(const char* expected, const T& actual)
+{
+ std::ostringstream str;
+ str << actual;
+ ATF_REQUIRE_EQ(expected, str.str());
+}
+
+
+} // anonymous namespace
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_map__empty);
+ATF_TEST_CASE_BODY(std_map__empty)
+{
+ do_check("map()", std::map< char, char >());
+ do_check("map()", std::map< int, long >());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_map__some);
+ATF_TEST_CASE_BODY(std_map__some)
+{
+ {
+ std::map< char, int > v;
+ v['b'] = 123;
+ v['z'] = 321;
+ do_check("map(b=123, z=321)", v);
+ }
+
+ {
+ std::map< int, std::string > v;
+ v[5] = "first";
+ v[2] = "second";
+ v[8] = "third";
+ do_check("map(2=second, 5=first, 8=third)", v);
+ }
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_pair);
+ATF_TEST_CASE_BODY(std_pair)
+{
+ do_check("pair(5, b)", std::pair< int, char >(5, 'b'));
+ do_check("pair(foo bar, baz)",
+ std::pair< std::string, std::string >("foo bar", "baz"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_shared_ptr__null);
+ATF_TEST_CASE_BODY(std_shared_ptr__null)
+{
+ do_check("<NULL>", std::shared_ptr< char >());
+ do_check("<NULL>", std::shared_ptr< int >());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_shared_ptr__not_null);
+ATF_TEST_CASE_BODY(std_shared_ptr__not_null)
+{
+ do_check("f", std::shared_ptr< char >(new char('f')));
+ do_check("8", std::shared_ptr< int >(new int(8)));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_set__empty);
+ATF_TEST_CASE_BODY(std_set__empty)
+{
+ do_check("set()", std::set< char >());
+ do_check("set()", std::set< int >());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_set__some);
+ATF_TEST_CASE_BODY(std_set__some)
+{
+ {
+ std::set< char > v;
+ v.insert('b');
+ v.insert('z');
+ do_check("set(b, z)", v);
+ }
+
+ {
+ std::set< int > v;
+ v.insert(5);
+ v.insert(2);
+ v.insert(8);
+ do_check("set(2, 5, 8)", v);
+ }
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_vector__empty);
+ATF_TEST_CASE_BODY(std_vector__empty)
+{
+ do_check("[]", std::vector< char >());
+ do_check("[]", std::vector< int >());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(std_vector__some);
+ATF_TEST_CASE_BODY(std_vector__some)
+{
+ {
+ std::vector< char > v;
+ v.push_back('b');
+ v.push_back('z');
+ do_check("[b, z]", v);
+ }
+
+ {
+ std::vector< int > v;
+ v.push_back(5);
+ v.push_back(2);
+ v.push_back(8);
+ do_check("[5, 2, 8]", v);
+ }
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, std_map__empty);
+ ATF_ADD_TEST_CASE(tcs, std_map__some);
+
+ ATF_ADD_TEST_CASE(tcs, std_pair);
+
+ ATF_ADD_TEST_CASE(tcs, std_shared_ptr__null);
+ ATF_ADD_TEST_CASE(tcs, std_shared_ptr__not_null);
+
+ ATF_ADD_TEST_CASE(tcs, std_set__empty);
+ ATF_ADD_TEST_CASE(tcs, std_set__some);
+
+ ATF_ADD_TEST_CASE(tcs, std_vector__empty);
+ ATF_ADD_TEST_CASE(tcs, std_vector__some);
+}
diff --git a/utils/format/exceptions.cpp b/utils/format/exceptions.cpp
new file mode 100644
index 000000000000..299b1d23cd8d
--- /dev/null
+++ b/utils/format/exceptions.cpp
@@ -0,0 +1,110 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "utils/format/exceptions.hpp"
+
+using utils::format::bad_format_error;
+using utils::format::error;
+using utils::format::extra_args_error;
+
+
+/// Constructs a new error with a plain-text message.
+///
+/// \param message The plain-text error message.
+error::error(const std::string& message) :
+ std::runtime_error(message)
+{
+}
+
+
+/// Destructor for the error.
+error::~error(void) throw()
+{
+}
+
+
+/// Constructs a new bad_format_error.
+///
+/// \param format_ The invalid format string.
+/// \param message Description of the error in the format string.
+bad_format_error::bad_format_error(const std::string& format_,
+ const std::string& message) :
+ error("Invalid formatting string '" + format_ + "': " + message),
+ _format(format_)
+{
+}
+
+
+/// Destructor for the error.
+bad_format_error::~bad_format_error(void) throw()
+{
+}
+
+
+/// \return The format string that caused the error.
+const std::string&
+bad_format_error::format(void) const
+{
+ return _format;
+}
+
+
+/// Constructs a new extra_args_error.
+///
+/// \param format_ The format string.
+/// \param arg_ The first extra argument passed to the format string.
+extra_args_error::extra_args_error(const std::string& format_,
+ const std::string& arg_) :
+ error("Not enough fields in formatting string '" + format_ + "' to place "
+ "argument '" + arg_ + "'"),
+ _format(format_),
+ _arg(arg_)
+{
+}
+
+
+/// Destructor for the error.
+extra_args_error::~extra_args_error(void) throw()
+{
+}
+
+
+/// \return The format string that was passed too many arguments.
+const std::string&
+extra_args_error::format(void) const
+{
+ return _format;
+}
+
+
+/// \return The first argument that caused the error.
+const std::string&
+extra_args_error::arg(void) const
+{
+ return _arg;
+}
diff --git a/utils/format/exceptions.hpp b/utils/format/exceptions.hpp
new file mode 100644
index 000000000000..a28376df9c08
--- /dev/null
+++ b/utils/format/exceptions.hpp
@@ -0,0 +1,84 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file utils/format/exceptions.hpp
+/// Exception types raised by the format module.
+
+#if !defined(UTILS_FORMAT_EXCEPTIONS_HPP)
+#define UTILS_FORMAT_EXCEPTIONS_HPP
+
+#include <stdexcept>
+#include <string>
+
+namespace utils {
+namespace format {
+
+
+/// Base exception for format errors.
+class error : public std::runtime_error {
+public:
+ explicit error(const std::string&);
+ virtual ~error(void) throw();
+};
+
+
+/// Error denoting a bad format string.
+class bad_format_error : public error {
+ /// The format string that caused the error.
+ std::string _format;
+
+public:
+ explicit bad_format_error(const std::string&, const std::string&);
+ virtual ~bad_format_error(void) throw();
+
+ const std::string& format(void) const;
+};
+
+
+/// Error denoting too many arguments for the format string.
+class extra_args_error : public error {
+ /// The format string that was passed too many arguments.
+ std::string _format;
+
+ /// The first argument that caused the error.
+ std::string _arg;
+
+public:
+ explicit extra_args_error(const std::string&, const std::string&);
+ virtual ~extra_args_error(void) throw();
+
+ const std::string& format(void) const;
+ const std::string& arg(void) const;
+};
+
+
+} // namespace format
+} // namespace utils
+
+
+#endif // !defined(UTILS_FORMAT_EXCEPTIONS_HPP)
diff --git a/utils/format/exceptions_test.cpp b/utils/format/exceptions_test.cpp
new file mode 100644
index 000000000000..28d401e57dad
--- /dev/null
+++ b/utils/format/exceptions_test.cpp
@@ -0,0 +1,74 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "utils/format/exceptions.hpp"
+
+#include <cstring>
+
+#include <atf-c++.hpp>
+
+using utils::format::bad_format_error;
+using utils::format::error;
+using utils::format::extra_args_error;
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(error);
+ATF_TEST_CASE_BODY(error)
+{
+ const error e("Some text");
+ ATF_REQUIRE(std::strcmp("Some text", e.what()) == 0);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(bad_format_error);
+ATF_TEST_CASE_BODY(bad_format_error)
+{
+ const bad_format_error e("format-string", "the-error");
+ ATF_REQUIRE(std::strcmp("Invalid formatting string 'format-string': "
+ "the-error", e.what()) == 0);
+ ATF_REQUIRE_EQ("format-string", e.format());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(extra_args_error);
+ATF_TEST_CASE_BODY(extra_args_error)
+{
+ const extra_args_error e("fmt", "extra");
+ ATF_REQUIRE(std::strcmp("Not enough fields in formatting string 'fmt' to "
+ "place argument 'extra'", e.what()) == 0);
+ ATF_REQUIRE_EQ("fmt", e.format());
+ ATF_REQUIRE_EQ("extra", e.arg());
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, error);
+ ATF_ADD_TEST_CASE(tcs, bad_format_error);
+ ATF_ADD_TEST_CASE(tcs, extra_args_error);
+}
diff --git a/utils/format/formatter.cpp b/utils/format/formatter.cpp
new file mode 100644
index 000000000000..99cfd40f03ab
--- /dev/null
+++ b/utils/format/formatter.cpp
@@ -0,0 +1,293 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "utils/format/formatter.hpp"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "utils/format/exceptions.hpp"
+#include "utils/sanity.hpp"
+#include "utils/text/exceptions.hpp"
+#include "utils/text/operations.ipp"
+
+namespace format = utils::format;
+namespace text = utils::text;
+
+
+namespace {
+
+
+/// Finds the next placeholder in a string.
+///
+/// \param format The original format string provided by the user; needed for
+/// error reporting purposes only.
+/// \param expansion The string containing the placeholder to look for. Any
+/// '%%' in the string will be skipped, and they must be stripped later by
+/// strip_double_percent().
+/// \param begin The position from which to start looking for the next
+/// placeholder.
+///
+/// \return The position in the string in which the placeholder is located and
+/// the placeholder itself. If there are no placeholders left, this returns
+/// the length of the string and an empty string.
+///
+/// \throw bad_format_error If the input string contains a trailing formatting
+/// character. We cannot detect any other kind of invalid formatter because
+/// we do not implement a full parser for them.
+static std::pair< std::string::size_type, std::string >
+find_next_placeholder(const std::string& format,
+ const std::string& expansion,
+ std::string::size_type begin)
+{
+ begin = expansion.find('%', begin);
+ while (begin != std::string::npos && expansion[begin + 1] == '%')
+ begin = expansion.find('%', begin + 2);
+ if (begin == std::string::npos)
+ return std::make_pair(expansion.length(), "");
+ if (begin == expansion.length() - 1)
+ throw format::bad_format_error(format, "Trailing %");
+
+ std::string::size_type end = begin + 1;
+ while (end < expansion.length() && expansion[end] != 's')
+ end++;
+ const std::string placeholder = expansion.substr(begin, end - begin + 1);
+ if (end == expansion.length() ||
+ placeholder.find('%', 1) != std::string::npos)
+ throw format::bad_format_error(format, "Unterminated placeholder '" +
+ placeholder + "'");
+ return std::make_pair(begin, placeholder);
+}
+
+
+/// Converts a string to an integer.
+///
+/// \param format The format string; for error reporting purposes only.
+/// \param str The string to conver.
+/// \param what The name of the field this integer belongs to; for error
+/// reporting purposes only.
+///
+/// \return An integer representing the input string.
+inline int
+to_int(const std::string& format, const std::string& str, const char* what)
+{
+ try {
+ return text::to_type< int >(str);
+ } catch (const text::value_error& e) {
+ throw format::bad_format_error(format, "Invalid " + std::string(what) +
+ "specifier");
+ }
+}
+
+
+/// Constructs an std::ostringstream based on a formatting placeholder.
+///
+/// \param format The format placeholder; may be empty.
+///
+/// \return A new std::ostringstream that is prepared to format a single
+/// object in the manner specified by the format placeholder.
+///
+/// \throw bad_format_error If the format string is bad. We do minimal
+/// validation on this string though.
+static std::ostringstream*
+new_ostringstream(const std::string& format)
+{
+ std::auto_ptr< std::ostringstream > output(new std::ostringstream());
+
+ if (format.length() <= 2) {
+ // If the format is empty, we create a new stream so that we don't have
+ // to check for NULLs later on. We rarely should hit this condition
+ // (and when we do it's a bug in the caller), so this is not a big deal.
+ //
+ // Otherwise, if the format is a regular '%s', then we don't have to do
+ // any processing for additional formatters. So this is just a "fast
+ // path".
+ } else {
+ std::string partial = format.substr(1, format.length() - 2);
+ if (partial[0] == '0') {
+ output->fill('0');
+ partial.erase(0, 1);
+ }
+ if (!partial.empty()) {
+ const std::string::size_type dot = partial.find('.');
+ if (dot != 0)
+ output->width(to_int(format, partial.substr(0, dot), "width"));
+ if (dot != std::string::npos) {
+ output->setf(std::ios::fixed, std::ios::floatfield);
+ output->precision(to_int(format, partial.substr(dot + 1),
+ "precision"));
+ }
+ }
+ }
+
+ return output.release();
+}
+
+
+/// Replaces '%%' by '%' in a given string range.
+///
+/// \param in The input string to be rewritten.
+/// \param begin The position at which to start the replacement.
+/// \param end The position at which to end the replacement.
+///
+/// \return The modified string and the amount of characters removed.
+static std::pair< std::string, int >
+strip_double_percent(const std::string& in, const std::string::size_type begin,
+ std::string::size_type end)
+{
+ std::string part = in.substr(begin, end - begin);
+
+ int removed = 0;
+ std::string::size_type pos = part.find("%%");
+ while (pos != std::string::npos) {
+ part.erase(pos, 1);
+ ++removed;
+ pos = part.find("%%", pos + 1);
+ }
+
+ return std::make_pair(in.substr(0, begin) + part + in.substr(end), removed);
+}
+
+
+} // anonymous namespace
+
+
+/// Performs internal initialization of the formatter.
+///
+/// This is separate from the constructor just because it is shared by different
+/// overloaded constructors.
+void
+format::formatter::init(void)
+{
+ const std::pair< std::string::size_type, std::string > placeholder =
+ find_next_placeholder(_format, _expansion, _last_pos);
+ const std::pair< std::string, int > no_percents =
+ strip_double_percent(_expansion, _last_pos, placeholder.first);
+
+ _oss = new_ostringstream(placeholder.second);
+
+ _expansion = no_percents.first;
+ _placeholder_pos = placeholder.first - no_percents.second;
+ _placeholder = placeholder.second;
+}
+
+
+/// Constructs a new formatter object (internal).
+///
+/// \param format The format string.
+/// \param expansion The format string with any replacements performed so far.
+/// \param last_pos The position from which to start looking for formatting
+/// placeholders. This must be maintained in case one of the replacements
+/// introduced a new placeholder, which must be ignored. Think, for
+/// example, replacing a "%s" string with "foo %s".
+format::formatter::formatter(const std::string& format,
+ const std::string& expansion,
+ const std::string::size_type last_pos) :
+ _format(format),
+ _expansion(expansion),
+ _last_pos(last_pos),
+ _oss(NULL)
+{
+ init();
+}
+
+
+/// Constructs a new formatter object.
+///
+/// \param format The format string. The formatters in the string are not
+/// validated during construction, but will cause errors when used later if
+/// they are invalid.
+format::formatter::formatter(const std::string& format) :
+ _format(format),
+ _expansion(format),
+ _last_pos(0),
+ _oss(NULL)
+{
+ init();
+}
+
+
+format::formatter::~formatter(void)
+{
+ delete _oss;
+}
+
+
+/// Returns the formatted string.
+///
+/// \return A string representation of the formatted string.
+const std::string&
+format::formatter::str(void) const
+{
+ return _expansion;
+}
+
+
+/// Automatic conversion of formatter objects to strings.
+///
+/// This is provided to allow painless injection of formatter objects into
+/// streams, without having to manually call the str() method.
+format::formatter::operator const std::string&(void) const
+{
+ return _expansion;
+}
+
+
+/// Specialization of operator% for booleans.
+///
+/// \param value The boolean to inject into the format string.
+///
+/// \return A new formatter that has one less format placeholder.
+format::formatter
+format::formatter::operator%(const bool& value) const
+{
+ (*_oss) << (value ? "true" : "false");
+ return replace(_oss->str());
+}
+
+
+/// Replaces the first formatting placeholder with a value.
+///
+/// \param arg The replacement string.
+///
+/// \return A new formatter in which the first formatting placeholder has been
+/// replaced by arg and is ready to replace the next item.
+///
+/// \throw utils::format::extra_args_error If there are no more formatting
+/// placeholders in the input string, or if the placeholder is invalid.
+format::formatter
+format::formatter::replace(const std::string& arg) const
+{
+ if (_placeholder_pos == _expansion.length())
+ throw format::extra_args_error(_format, arg);
+
+ const std::string expansion = _expansion.substr(0, _placeholder_pos)
+ + arg + _expansion.substr(_placeholder_pos + _placeholder.length());
+ return formatter(_format, expansion, _placeholder_pos + arg.length());
+}
diff --git a/utils/format/formatter.hpp b/utils/format/formatter.hpp
new file mode 100644
index 000000000000..8c6188745a2e
--- /dev/null
+++ b/utils/format/formatter.hpp
@@ -0,0 +1,123 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file utils/format/formatter.hpp
+/// Provides the definition of the utils::format::formatter class.
+///
+/// The utils::format::formatter class is a poor man's replacement for the
+/// Boost.Format library, as it is much simpler and has less dependencies.
+///
+/// Be aware that the formatting supported by this module is NOT compatible
+/// with printf(3) nor with Boost.Format. The general syntax for a
+/// placeholder in a formatting string is:
+///
+/// %[0][width][.precision]s
+///
+/// In particular, note that the only valid formatting specifier is %s: the
+/// library deduces what to print based on the type of the variable passed
+/// in, not based on what the format string says. Also, note that the only
+/// valid padding character is 0.
+
+#if !defined(UTILS_FORMAT_FORMATTER_HPP)
+#define UTILS_FORMAT_FORMATTER_HPP
+
+#include "utils/format/formatter_fwd.hpp"
+
+#include <sstream>
+#include <string>
+
+namespace utils {
+namespace format {
+
+
+/// Mechanism to format strings similar to printf.
+///
+/// A formatter always maintains the original format string but also holds a
+/// partial expansion. The partial expansion is immutable in the context of a
+/// formatter instance, but calls to operator% return new formatter objects with
+/// one less formatting placeholder.
+///
+/// In general, one can format a string in the following manner:
+///
+/// \code
+/// const std::string s = (formatter("%s %s") % "foo" % 5).str();
+/// \endcode
+///
+/// which, following the explanation above, would correspond to:
+///
+/// \code
+/// const formatter f1("%s %s");
+/// const formatter f2 = f1 % "foo";
+/// const formatter f3 = f2 % 5;
+/// const std::string s = f3.str();
+/// \endcode
+class formatter {
+ /// The original format string provided by the user.
+ std::string _format;
+
+ /// The current "expansion" of the format string.
+ ///
+ /// This field gets updated on every call to operator%() to have one less
+ /// formatting placeholder.
+ std::string _expansion;
+
+ /// The position of _expansion from which to scan for placeholders.
+ std::string::size_type _last_pos;
+
+ /// The position of the first placeholder in the current expansion.
+ std::string::size_type _placeholder_pos;
+
+ /// The first placeholder in the current expansion.
+ std::string _placeholder;
+
+ /// Stream used to format any possible argument supplied by operator%().
+ std::ostringstream* _oss;
+
+ formatter replace(const std::string&) const;
+
+ void init(void);
+ formatter(const std::string&, const std::string&,
+ const std::string::size_type);
+
+public:
+ explicit formatter(const std::string&);
+ ~formatter(void);
+
+ const std::string& str(void) const;
+ operator const std::string&(void) const;
+
+ template< typename Type > formatter operator%(const Type&) const;
+ formatter operator%(const bool&) const;
+};
+
+
+} // namespace format
+} // namespace utils
+
+
+#endif // !defined(UTILS_FORMAT_FORMATTER_HPP)
diff --git a/utils/format/formatter.ipp b/utils/format/formatter.ipp
new file mode 100644
index 000000000000..6fad024b704f
--- /dev/null
+++ b/utils/format/formatter.ipp
@@ -0,0 +1,76 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#if !defined(UTILS_FORMAT_FORMATTER_IPP)
+#define UTILS_FORMAT_FORMATTER_IPP
+
+#include <ostream>
+
+#include "utils/format/formatter.hpp"
+
+namespace utils {
+namespace format {
+
+
+/// Replaces the first format placeholder in a formatter.
+///
+/// Constructs a new formatter object that has one less formatting placeholder,
+/// as this has been replaced by the provided argument. Calling this operator
+/// N times, where N is the number of formatting placeholders, effectively
+/// formats the string.
+///
+/// \param arg The argument to use as replacement for the format placeholder.
+///
+/// \return A new formatter that has one less format placeholder.
+template< typename Type >
+inline formatter
+formatter::operator%(const Type& arg) const
+{
+ (*_oss) << arg;
+ return replace(_oss->str());
+}
+
+
+/// Inserts a formatter string into a stream.
+///
+/// \param os The output stream.
+/// \param f The formatter to process and inject into the stream.
+///
+/// \return The output stream os.
+inline std::ostream&
+operator<<(std::ostream& os, const formatter& f)
+{
+ return (os << f.str());
+}
+
+
+} // namespace format
+} // namespace utils
+
+
+#endif // !defined(UTILS_FORMAT_FORMATTER_IPP)
diff --git a/utils/format/formatter_fwd.hpp b/utils/format/formatter_fwd.hpp
new file mode 100644
index 000000000000..72c9e5ebf196
--- /dev/null
+++ b/utils/format/formatter_fwd.hpp
@@ -0,0 +1,45 @@
+// Copyright 2015 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file utils/format/formatter_fwd.hpp
+/// Forward declarations for utils/format/formatter.hpp
+
+#if !defined(UTILS_FORMAT_FORMATTER_FWD_HPP)
+#define UTILS_FORMAT_FORMATTER_FWD_HPP
+
+namespace utils {
+namespace format {
+
+
+class formatter;
+
+
+} // namespace format
+} // namespace utils
+
+#endif // !defined(UTILS_FORMAT_FORMATTER_FWD_HPP)
diff --git a/utils/format/formatter_test.cpp b/utils/format/formatter_test.cpp
new file mode 100644
index 000000000000..fdae785b1db7
--- /dev/null
+++ b/utils/format/formatter_test.cpp
@@ -0,0 +1,265 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "utils/format/formatter.hpp"
+
+#include <ostream>
+
+#include <atf-c++.hpp>
+
+#include "utils/format/exceptions.hpp"
+#include "utils/format/macros.hpp"
+
+namespace format = utils::format;
+
+
+namespace {
+
+
+/// Wraps an integer in a C++ class.
+///
+/// This custom type exists to ensure that we can feed arbitrary objects that
+/// support operator<< to the formatter;
+class int_wrapper {
+ /// The wrapped integer.
+ int _value;
+
+public:
+ /// Constructs a new wrapper.
+ ///
+ /// \param value_ The value to wrap.
+ int_wrapper(const int value_) : _value(value_)
+ {
+ }
+
+ /// Returns the wrapped value.
+ ///
+ /// \return An integer.
+ int
+ value(void) const
+ {
+ return _value;
+ }
+};
+
+
+/// Writes a wrapped integer into an output stream.
+///
+/// \param output The output stream into which to place the integer.
+/// \param wrapper The wrapped integer.
+///
+/// \return The output stream.
+std::ostream&
+operator<<(std::ostream& output, const int_wrapper& wrapper)
+{
+ return (output << wrapper.value());
+}
+
+
+} // anonymous namespace
+
+
+/// Calls ATF_REQUIRE_EQ on an expected string and a formatter.
+///
+/// This is pure syntactic sugar to avoid calling the str() method on all the
+/// individual tests below, which results in very long lines that require
+/// wrapping and clutter readability.
+///
+/// \param expected The expected string generated by the formatter.
+/// \param formatter The formatter to test.
+#define EQ(expected, formatter) ATF_REQUIRE_EQ(expected, (formatter).str())
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(no_fields);
+ATF_TEST_CASE_BODY(no_fields)
+{
+ EQ("Plain string", F("Plain string"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(one_field);
+ATF_TEST_CASE_BODY(one_field)
+{
+ EQ("foo", F("%sfoo") % "");
+ EQ(" foo", F("%sfoo") % " ");
+ EQ("foo ", F("foo %s") % "");
+ EQ("foo bar", F("foo %s") % "bar");
+ EQ("foo bar baz", F("foo %s baz") % "bar");
+ EQ("foo %s %s", F("foo %s %s") % "%s" % "%s");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(many_fields);
+ATF_TEST_CASE_BODY(many_fields)
+{
+ EQ("", F("%s%s") % "" % "");
+ EQ("foo", F("%s%s%s") % "" % "foo" % "");
+ EQ("some 5 text", F("%s %s %s") % "some" % 5 % "text");
+ EQ("f%s 5 text", F("%s %s %s") % "f%s" % 5 % "text");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(escape);
+ATF_TEST_CASE_BODY(escape)
+{
+ EQ("%", F("%%"));
+ EQ("% %", F("%% %%"));
+ EQ("%% %%", F("%%%% %%%%"));
+
+ EQ("foo %", F("foo %%"));
+ EQ("foo bar %", F("foo %s %%") % "bar");
+ EQ("foo % bar", F("foo %% %s") % "bar");
+
+ EQ("foo %%", F("foo %s") % "%%");
+ EQ("foo a%%b", F("foo a%sb") % "%%");
+ EQ("foo a%%b", F("foo %s") % "a%%b");
+
+ EQ("foo % bar %%", F("foo %% %s %%%%") % "bar");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(extra_args_error);
+ATF_TEST_CASE_BODY(extra_args_error)
+{
+ using format::extra_args_error;
+
+ ATF_REQUIRE_THROW(extra_args_error, F("foo") % "bar");
+ ATF_REQUIRE_THROW(extra_args_error, F("foo %%") % "bar");
+ ATF_REQUIRE_THROW(extra_args_error, F("foo %s") % "bar" % "baz");
+ ATF_REQUIRE_THROW(extra_args_error, F("foo %s") % "%s" % "bar");
+ ATF_REQUIRE_THROW(extra_args_error, F("%s foo %s") % "bar" % "baz" % "foo");
+
+ try {
+ F("foo %s %s") % "bar" % "baz" % "something extra";
+ fail("extra_args_error not raised");
+ } catch (const extra_args_error& e) {
+ ATF_REQUIRE_EQ("foo %s %s", e.format());
+ ATF_REQUIRE_EQ("something extra", e.arg());
+ }
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__class);
+ATF_TEST_CASE_BODY(format__class)
+{
+ EQ("foo bar", F("%s") % std::string("foo bar"));
+ EQ("3", F("%s") % int_wrapper(3));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__pointer);
+ATF_TEST_CASE_BODY(format__pointer)
+{
+ EQ("0xcafebabe", F("%s") % reinterpret_cast< void* >(0xcafebabe));
+ EQ("foo bar", F("%s") % "foo bar");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__bool);
+ATF_TEST_CASE_BODY(format__bool)
+{
+ EQ("true", F("%s") % true);
+ EQ("false", F("%s") % false);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__char);
+ATF_TEST_CASE_BODY(format__char)
+{
+ EQ("Z", F("%s") % 'Z');
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__float);
+ATF_TEST_CASE_BODY(format__float)
+{
+ EQ("3", F("%s") % 3.0);
+ EQ("3.0", F("%.1s") % 3.0);
+ EQ("3.0", F("%0.1s") % 3.0);
+ EQ(" 15.600", F("%8.3s") % 15.6);
+ EQ("01.52", F("%05.2s") % 1.52);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__int);
+ATF_TEST_CASE_BODY(format__int)
+{
+ EQ("3", F("%s") % 3);
+ EQ("3", F("%0s") % 3);
+ EQ(" -123", F("%5s") % -123);
+ EQ("00078", F("%05s") % 78);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(format__error);
+ATF_TEST_CASE_BODY(format__error)
+{
+ using format::bad_format_error;
+
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("%"));
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("f%"));
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("f%%%"));
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("ab %s cd%") % "cd");
+
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid width", F("%1bs"));
+
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%.s"));
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%0.s"));
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%123.s"));
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%.12bs"));
+
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%c") % 'Z');
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%d") % 5);
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%.1f") % 3);
+ ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%d%s") % 3 % "a");
+
+ try {
+ F("foo %s%") % "bar";
+ fail("bad_format_error not raised");
+ } catch (const bad_format_error& e) {
+ ATF_REQUIRE_EQ("foo %s%", e.format());
+ }
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, no_fields);
+ ATF_ADD_TEST_CASE(tcs, one_field);
+ ATF_ADD_TEST_CASE(tcs, many_fields);
+ ATF_ADD_TEST_CASE(tcs, escape);
+ ATF_ADD_TEST_CASE(tcs, extra_args_error);
+
+ ATF_ADD_TEST_CASE(tcs, format__class);
+ ATF_ADD_TEST_CASE(tcs, format__pointer);
+ ATF_ADD_TEST_CASE(tcs, format__bool);
+ ATF_ADD_TEST_CASE(tcs, format__char);
+ ATF_ADD_TEST_CASE(tcs, format__float);
+ ATF_ADD_TEST_CASE(tcs, format__int);
+ ATF_ADD_TEST_CASE(tcs, format__error);
+}
diff --git a/utils/format/macros.hpp b/utils/format/macros.hpp
new file mode 100644
index 000000000000..09ef14ea485e
--- /dev/null
+++ b/utils/format/macros.hpp
@@ -0,0 +1,58 @@
+// Copyright 2010 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file utils/format/macros.hpp
+/// Convenience macros to simplify usage of the format library.
+///
+/// This file <em>must not be included from other header files</em>.
+
+#if !defined(UTILS_FORMAT_MACROS_HPP)
+#define UTILS_FORMAT_MACROS_HPP
+
+// We include the .ipp file instead of .hpp because, after all, macros.hpp
+// is provided purely for convenience and must not be included from other
+// header files. Henceforth, we make things easier to the callers.
+#include "utils/format/formatter.ipp"
+
+
+/// Constructs a utils::format::formatter object with the given format string.
+///
+/// This macro is just a wrapper to make the construction of
+/// utils::format::formatter objects shorter, and thus to allow inlining these
+/// calls right in where formatted strings are required. A typical usage would
+/// look like:
+///
+/// \code
+/// std::cout << F("%s %d\n") % my_str % my_int;
+/// \endcode
+///
+/// \param fmt The format string.
+#define F(fmt) utils::format::formatter(fmt)
+
+
+#endif // !defined(UTILS_FORMAT_MACROS_HPP)