aboutsummaryrefslogtreecommitdiff
path: root/0.4.0/paludis/util
diff options
context:
space:
mode:
Diffstat (limited to '0.4.0/paludis/util')
-rw-r--r--0.4.0/paludis/util/Makefile.am.m467
-rw-r--r--0.4.0/paludis/util/attributes.hh54
-rw-r--r--0.4.0/paludis/util/collection.hh294
-rw-r--r--0.4.0/paludis/util/compare.hh160
-rw-r--r--0.4.0/paludis/util/comparison_policy.hh.m4444
-rw-r--r--0.4.0/paludis/util/comparison_policy_TEST.cc44
-rw-r--r--0.4.0/paludis/util/composite_pattern.hh66
-rw-r--r--0.4.0/paludis/util/container_entry.hh77
-rw-r--r--0.4.0/paludis/util/container_entry_TEST.cc65
-rw-r--r--0.4.0/paludis/util/counted_ptr.cc28
-rw-r--r--0.4.0/paludis/util/counted_ptr.hh606
-rw-r--r--0.4.0/paludis/util/counted_ptr_TEST.cc390
-rw-r--r--0.4.0/paludis/util/deleter.cc32
-rw-r--r--0.4.0/paludis/util/deleter.hh57
-rw-r--r--0.4.0/paludis/util/deleter_TEST.cc85
-rw-r--r--0.4.0/paludis/util/destringify.cc34
-rw-r--r--0.4.0/paludis/util/destringify.hh149
-rw-r--r--0.4.0/paludis/util/destringify_TEST.cc113
-rw-r--r--0.4.0/paludis/util/dir_iterator.cc143
-rw-r--r--0.4.0/paludis/util/dir_iterator.hh127
-rw-r--r--0.4.0/paludis/util/dir_iterator_TEST.cc97
-rwxr-xr-x0.4.0/paludis/util/dir_iterator_TEST_cleanup.sh9
-rwxr-xr-x0.4.0/paludis/util/dir_iterator_TEST_setup.sh6
-rw-r--r--0.4.0/paludis/util/exception.cc64
-rw-r--r--0.4.0/paludis/util/exception.hh158
-rw-r--r--0.4.0/paludis/util/files.m441
-rw-r--r--0.4.0/paludis/util/fs_entry.cc439
-rw-r--r--0.4.0/paludis/util/fs_entry.hh319
-rw-r--r--0.4.0/paludis/util/fs_entry_TEST.cc242
-rwxr-xr-x0.4.0/paludis/util/fs_entry_TEST_cleanup.sh9
-rwxr-xr-x0.4.0/paludis/util/fs_entry_TEST_setup.sh17
-rw-r--r--0.4.0/paludis/util/instantiation_policy.hh207
-rw-r--r--0.4.0/paludis/util/instantiation_policy_TEST.cc136
-rw-r--r--0.4.0/paludis/util/is_file_with_extension.cc44
-rw-r--r--0.4.0/paludis/util/is_file_with_extension.hh75
-rw-r--r--0.4.0/paludis/util/is_file_with_extension_TEST.cc94
-rwxr-xr-x0.4.0/paludis/util/is_file_with_extension_TEST_cleanup.sh9
-rwxr-xr-x0.4.0/paludis/util/is_file_with_extension_TEST_setup.sh6
-rw-r--r--0.4.0/paludis/util/iterator.hh536
-rw-r--r--0.4.0/paludis/util/iterator_TEST.cc339
-rw-r--r--0.4.0/paludis/util/join.hh67
-rw-r--r--0.4.0/paludis/util/join_TEST.cc102
-rw-r--r--0.4.0/paludis/util/log.cc151
-rw-r--r--0.4.0/paludis/util/log.hh121
-rw-r--r--0.4.0/paludis/util/log_TEST.cc74
-rw-r--r--0.4.0/paludis/util/private_implementation_pattern.hh70
-rw-r--r--0.4.0/paludis/util/pstream.cc99
-rw-r--r--0.4.0/paludis/util/pstream.hh193
-rw-r--r--0.4.0/paludis/util/pstream_TEST.cc111
-rw-r--r--0.4.0/paludis/util/random.cc43
-rw-r--r--0.4.0/paludis/util/random.hh61
-rw-r--r--0.4.0/paludis/util/random_TEST.cc121
-rw-r--r--0.4.0/paludis/util/save.hh78
-rw-r--r--0.4.0/paludis/util/save_TEST.cc68
-rw-r--r--0.4.0/paludis/util/smart_record.hh.m4924
-rw-r--r--0.4.0/paludis/util/smart_record_TEST.cc200
-rw-r--r--0.4.0/paludis/util/stringify.hh168
-rw-r--r--0.4.0/paludis/util/stringify_TEST.cc129
-rw-r--r--0.4.0/paludis/util/strip.cc99
-rw-r--r--0.4.0/paludis/util/strip.hh127
-rw-r--r--0.4.0/paludis/util/strip_TEST.cc123
-rw-r--r--0.4.0/paludis/util/system.cc186
-rw-r--r--0.4.0/paludis/util/system.hh148
-rw-r--r--0.4.0/paludis/util/system_TEST.cc183
-rwxr-xr-x0.4.0/paludis/util/system_TEST_cleanup.sh9
-rwxr-xr-x0.4.0/paludis/util/system_TEST_setup.sh5
-rw-r--r--0.4.0/paludis/util/test_extras.cc65
-rw-r--r--0.4.0/paludis/util/tokeniser.cc28
-rw-r--r--0.4.0/paludis/util/tokeniser.hh243
-rw-r--r--0.4.0/paludis/util/tokeniser_TEST.cc139
-rw-r--r--0.4.0/paludis/util/util.hh.m442
-rw-r--r--0.4.0/paludis/util/validated.hh111
-rw-r--r--0.4.0/paludis/util/validated_TEST.cc126
-rw-r--r--0.4.0/paludis/util/virtual_constructor.hh243
-rw-r--r--0.4.0/paludis/util/virtual_constructor_TEST.cc190
-rw-r--r--0.4.0/paludis/util/visitor.hh297
-rw-r--r--0.4.0/paludis/util/visitor_TEST.cc168
77 files changed, 11194 insertions, 0 deletions
diff --git a/0.4.0/paludis/util/Makefile.am.m4 b/0.4.0/paludis/util/Makefile.am.m4
new file mode 100644
index 000000000..71021e14d
--- /dev/null
+++ b/0.4.0/paludis/util/Makefile.am.m4
@@ -0,0 +1,67 @@
+ifdef(`__gnu__',`',`errprint(`This is not GNU m4...
+')m4exit(1)') include(`misc/generated-file.txt')
+
+dnl vim: set ft=m4 noet :
+
+define(`filelist', `')dnl
+define(`headerlist', `')dnl
+define(`testlist', `')dnl
+define(`testscriptlist', `')dnl
+define(`addtest', `define(`testlist', testlist `$1_TEST')dnl
+$1_TEST_SOURCES = $1_TEST.cc
+$1_TEST_LDADD = test_extras.o $(top_builddir)/test/libtest.a libpaludisutil.a
+$1_TEST_CXXFLAGS = -I$(top_srcdir)
+')dnl
+define(`addtestscript', `define(`testscriptlist', testscriptlist `$1_TEST_setup.sh $1_TEST_cleanup.sh')')dnl
+define(`addhh', `define(`filelist', filelist `$1.hh')define(`headerlist', headerlist `$1.hh')')dnl
+define(`addcc', `define(`filelist', filelist `$1.cc')')dnl
+define(`addimpl', `define(`filelist', filelist `$1-impl.hh')')dnl
+define(`addthis', `dnl
+ifelse(`$2', `hh', `addhh(`$1')', `')dnl
+ifelse(`$2', `cc', `addcc(`$1')', `')dnl
+ifelse(`$2', `impl', `addimpl(`$1')', `')dnl
+ifelse(`$2', `test', `addtest(`$1')', `')dnl
+ifelse(`$2', `testscript', `addtestscript(`$1')', `')')dnl
+define(`add', `addthis(`$1',`$2')addthis(`$1',`$3')addthis(`$1',`$4')dnl
+addthis(`$1',`$5')addthis(`$1',`$6')')dnl
+
+include(`paludis/util/files.m4')
+
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda
+MAINTAINERCLEANFILES = Makefile.in Makefile.am paludis.hh smart_record.hh \
+ hashed_containers.hh comparison_policy.hh util.hh
+AM_CXXFLAGS = -I$(top_srcdir)
+DEFS=\
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DLIBEXECDIR=\"$(libexecdir)\"
+EXTRA_DIST = util.hh.m4 Makefile.am.m4 files.m4 smart_record.hh.m4 \
+ comparison_policy.hh.m4 testscriptlist test_extras.cc
+SUBDIRS = .
+
+libpaludisutil_a_SOURCES = filelist
+
+TESTS = testlist
+
+TESTS_ENVIRONMENT = env \
+ PALUDIS_EBUILD_DIR="$(srcdir)/ebuild/" \
+ TEST_SCRIPT_DIR="$(srcdir)/" \
+ bash $(top_srcdir)/test/run_test.sh
+
+check_PROGRAMS = $(TESTS)
+check_SCRIPTS = testscriptlist
+lib_LIBRARIES = libpaludisutil.a
+paludis_util_includedir = $(includedir)/paludis/util/
+paludis_util_include_HEADERS = headerlist
+
+Makefile.am : Makefile.am.m4 files.m4
+ $(top_srcdir)/misc/do_m4.bash Makefile.am
+
+smart_record.hh : smart_record.hh.m4
+ $(top_srcdir)/misc/do_m4.bash smart_record.hh
+
+comparison_policy.hh : comparison_policy.hh.m4
+ $(top_srcdir)/misc/do_m4.bash comparison_policy.hh
+
+util.hh : util.hh.m4 files.m4
+ $(top_srcdir)/misc/do_m4.bash util.hh
+
diff --git a/0.4.0/paludis/util/attributes.hh b/0.4.0/paludis/util/attributes.hh
new file mode 100644
index 000000000..711a0a301
--- /dev/null
+++ b/0.4.0/paludis/util/attributes.hh
@@ -0,0 +1,54 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_ATTRIBUTES_HH
+#define PALUDIS_GUARD_PALUDIS_ATTRIBUTES_HH 1
+
+/** \file
+ * Declare the PALUDIS_ATTRIBUTE macro.
+ *
+ * \ingroup grplibpaludisutil
+ */
+
+/** \def PALUDIS_ATTRIBUTE
+ * If we're using a recent GCC or ICC, expands to __attribute__, otherwise
+ * discards its arguments.
+ *
+ * \ingroup grplibpaludisutil
+ */
+
+/** \def PALUDIS_CAN_USE_ATTRIBUTE
+ * Defined if we can rely upon PALUDIS_ATTRIBUTE working (for example, for
+ * weak).
+ *
+ * \ingroup grplibpaludisutil
+ */
+
+#if (defined(__GNUC__) || defined(DOXYGEN))
+# if ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || defined(DOXYGEN))
+# define PALUDIS_ATTRIBUTE(x) __attribute__(x)
+# define PALUDIS_CAN_USE_ATTRIBUTE 1
+# else
+# define PALUDIS_ATTRIBUTE(x)
+# endif
+#else
+# define PALUDIS_ATTRIBUTE(x)
+#endif
+
+#endif
diff --git a/0.4.0/paludis/util/collection.hh b/0.4.0/paludis/util/collection.hh
new file mode 100644
index 000000000..764d3f637
--- /dev/null
+++ b/0.4.0/paludis/util/collection.hh
@@ -0,0 +1,294 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_SEQUENTIAL_COLLECTION_HH
+#define PALUDIS_GUARD_PALUDIS_SEQUENTIAL_COLLECTION_HH 1
+
+#include <algorithm>
+#include <list>
+#include <set>
+#include <iterator>
+#include <paludis/util/counted_ptr.hh>
+#include <paludis/util/instantiation_policy.hh>
+
+/** \file
+ * Various wrappers around collections of items, for convenience and
+ * avoiding passing around huge containers.
+ *
+ * \ingroup grpcollections
+ */
+
+namespace paludis
+{
+ /**
+ * Wrapper around a std::list of a particular item. Multiple items
+ * with the same value are disallowed.
+ *
+ * \ingroup grpcollections
+ */
+ template <typename T_>
+ class SequentialCollection :
+ private InstantiationPolicy<SequentialCollection<T_>, instantiation_method::NonCopyableTag>,
+ public InternalCounted<SequentialCollection<T_> >,
+ public std::iterator<typename std::iterator_traits<
+ typename std::list<T_>::const_iterator>::iterator_category, T_>
+ {
+ private:
+ std::list<T_> _items;
+
+ public:
+ /**
+ * Issue with g++ 3.4.6: const_reference isn't defined via our std::iterator
+ * inherit, but it is needed by many standard algorithms.
+ */
+ typedef const T_ & const_reference;
+
+ /**
+ * Constructor.
+ */
+ SequentialCollection()
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ virtual ~SequentialCollection()
+ {
+ }
+
+ /**
+ * Our iterator type.
+ */
+ typedef typename std::list<T_>::const_iterator Iterator;
+
+ /**
+ * Iterator to the start of our items.
+ */
+ Iterator begin() const
+ {
+ return _items.begin();
+ }
+
+ /**
+ * Iterator to one past our last item.
+ */
+ Iterator end() const
+ {
+ return _items.end();
+ }
+
+ /**
+ * Iterator to our last item.
+ */
+ Iterator last() const
+ {
+ return _items.begin() == _items.end() ? _items.end() : --(_items.end());
+ }
+
+ /**
+ * Return an Iterator to an item, or end() if there's no match.
+ */
+ Iterator find(const T_ & v) const
+ {
+ return std::find(_items.begin(), _items.end(), v);
+ }
+
+ /**
+ * Append an item, return whether we succeeded.
+ */
+ bool append(T_ v)
+ {
+ if (end() != find(v))
+ return false;
+
+ _items.push_back(v);
+ return true;
+ }
+
+ /**
+ * Append an item.
+ */
+ void push_back(const T_ & v)
+ {
+ if (end() == find(v))
+ _items.push_back(v);
+ }
+
+ /**
+ * Are we empty?
+ */
+ bool empty() const
+ {
+ return _items.empty();
+ }
+ };
+
+ /**
+ * Wrapper around a std::set of a particular item. May be changed at some
+ * point to support template find.
+ *
+ * \ingroup grpcollections
+ */
+ template <typename T_>
+ class SortedCollection :
+ private InstantiationPolicy<SortedCollection<T_>, instantiation_method::NonCopyableTag>,
+ public InternalCounted<SortedCollection<T_> >,
+ public std::iterator<typename std::iterator_traits<
+ typename std::set<T_>::const_iterator>::iterator_category, T_>
+ {
+ private:
+ std::set<T_> _items;
+
+ public:
+ /**
+ * Constructor.
+ */
+ SortedCollection()
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ virtual ~SortedCollection()
+ {
+ }
+
+ /**
+ * Our iterator type.
+ */
+ typedef typename std::set<T_>::const_iterator Iterator;
+
+ /**
+ * Iterator to the start of our items.
+ */
+ Iterator begin() const
+ {
+ return _items.begin();
+ }
+
+ /**
+ * Iterator to one past our last item.
+ */
+ Iterator end() const
+ {
+ return _items.end();
+ }
+
+ /**
+ * Our reverse iterator type.
+ */
+ typedef typename std::set<T_>::const_reverse_iterator ReverseIterator;
+
+ /**
+ * Reverse iterator to the start of our items.
+ */
+ ReverseIterator rbegin() const
+ {
+ return _items.rbegin();
+ }
+
+ /**
+ * Reverse iterator to one past our last item.
+ */
+ ReverseIterator rend() const
+ {
+ return _items.rend();
+ }
+
+ /**
+ * Iterator to our last item.
+ */
+ Iterator last() const
+ {
+ Iterator result(_items.end());
+ if (result != _items.begin())
+ --result;
+ return result;
+ }
+
+ /**
+ * Return an Iterator to an item, or end() if there's no match.
+ */
+ Iterator find(const T_ & v) const
+ {
+ return _items.find(v);
+ }
+
+ /**
+ * Insert an item, return whether we succeeded.
+ */
+ bool insert(const T_ & v)
+ {
+ return _items.insert(v).second;
+ }
+
+ /**
+ * Erase an item, return whether we succeeded.
+ */
+ bool erase(const T_ & v)
+ {
+ return 0 != _items.erase(v);
+ }
+
+ /**
+ * Insert all items from another container.
+ */
+ bool merge(typename SortedCollection<T_>::ConstPointer o)
+ {
+ bool result(true);
+ Iterator o_begin(o->begin()), o_end(o->end());
+ for ( ; o_begin != o_end ; ++o_begin)
+ result &= insert(*o_begin);
+ return result;
+ }
+
+ /**
+ * Are we empty?
+ */
+ bool empty() const
+ {
+ return _items.empty();
+ }
+
+ /**
+ * How big are we?
+ */
+ unsigned size() const
+ {
+ return _items.size();
+ }
+
+ /**
+ * Our insert iterator type.
+ */
+ typedef typename std::insert_iterator<std::set<T_> > Inserter;
+
+ /**
+ * Fetch an inserter.
+ */
+ Inserter inserter()
+ {
+ return std::inserter(_items, _items.begin());
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/compare.hh b/0.4.0/paludis/util/compare.hh
new file mode 100644
index 000000000..3304b8566
--- /dev/null
+++ b/0.4.0/paludis/util/compare.hh
@@ -0,0 +1,160 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_COMPARE_HH
+#define PALUDIS_GUARD_PALUDIS_COMPARE_HH 1
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/validated.hh>
+#include <string>
+
+/** \file
+ * Declarations for the compare functions.
+ *
+ * \ingroup grpcompare
+ */
+
+namespace paludis
+{
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ inline int compare(int t1, int t2)
+ {
+ if (t1 < t2)
+ return -1;
+ else if (t1 > t2)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ inline int compare(unsigned t1, unsigned t2)
+ {
+ if (t1 < t2)
+ return -1;
+ else if (t1 > t2)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ inline int long compare(unsigned long t1, unsigned long t2)
+ {
+ if (t1 < t2)
+ return -1;
+ else if (t1 > t2)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ inline int compare(long t1, long t2)
+ {
+ if (t1 < t2)
+ return -1;
+ else if (t1 > t2)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename T_>
+ inline int compare(
+ const std::basic_string<T_> & t1,
+ const std::basic_string<T_> & t2)
+ {
+ register int r(t1.compare(t2));
+ if (r < 0)
+ return -1;
+ else if (r > 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename T_, typename U_>
+ inline int compare(
+ const Validated<T_, U_> & t1,
+ const Validated<T_, U_> & t2)
+ {
+ return compare(t1.data(), t2.data());
+ }
+
+ /**
+ * Compare t1 and t2.
+ *
+ * \retval -1 if t1 < t2, 1 if t1 > t2, 0 otherwise.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename T_>
+ int compare(
+ const T_ & t1,
+ const T_ & t2)
+ {
+ if (t1 < t2)
+ return -1;
+ else if (t1 > t2)
+ return 1;
+ else
+ return 0;
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/comparison_policy.hh.m4 b/0.4.0/paludis/util/comparison_policy.hh.m4
new file mode 100644
index 000000000..adb0e88c6
--- /dev/null
+++ b/0.4.0/paludis/util/comparison_policy.hh.m4
@@ -0,0 +1,444 @@
+#if 0
+ifdef(`__gnu__',`',`errprint(`This is not GNU m4...
+')m4exit(1)') include(`misc/generated-file.txt')
+dnl vim: set ft=cpp et sw=4 sts=4 :
+#endif
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_COMPARISON_POLICY_HH
+#define PALUDIS_GUARD_PALUDIS_COMPARISON_POLICY_HH 1
+
+/** \file
+ * Declarations for the ComparisonPolicy class.
+ *
+ * \ingroup grpcompare
+ */
+
+namespace paludis
+{
+ /**
+ * Comparison modes for paludis::ComparisonPolicy.
+ *
+ * \ingroup grpcompare
+ */
+ namespace comparison_mode
+ {
+ /**
+ * No comparisons can be made.
+ *
+ * \ingroup grpcompare
+ */
+ struct NoComparisonTag
+ {
+ };
+
+ /**
+ * Comparisons can be made via operator== and operator!=.
+ *
+ * \ingroup grpcompare
+ */
+ struct EqualityComparisonTag
+ {
+ };
+
+ /**
+ * The full range of comparison operators is available.
+ *
+ * \ingroup grpcompare
+ */
+ struct FullComparisonTag
+ {
+ };
+ }
+
+ /**
+ * Comparison methods for paludis::ComparisonPolicy.
+ *
+ * \ingroup grpcompare
+ */
+ namespace comparison_method
+ {
+ /**
+ * Comparisons are done via a member of type MemberType_.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename MemberType_>
+ struct CompareByMemberTag
+ {
+ };
+
+ /**
+ * Comparisons are done by a member function that returns an integer
+ * less than zero (less than), equal to zero (equal to) or greater than
+ * zero (greater than).
+ *
+ * \ingroup grpcompare
+ */
+ struct CompareByMemberComparisonFunctionTag
+ {
+ };
+
+
+ /**
+ * Comparisons are done via a member function that returns an item of
+ * type MemberType_.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename MemberType_>
+ struct CompareByMemberFetchFunctionTag
+ {
+ };
+ }
+
+#ifdef DOXYGEN
+ /**
+ * ComparisonPolicy specifies the availabillity of comparison methods and
+ * the strategy used to do comparisons.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_, typename ComparisonModeTag_, typename ComparisonMethodTag_>
+ struct ComparisonPolicy
+ {
+ };
+#else
+ template <typename OurType_, typename ComparisonModeTag_, typename ComparisonMethodTag_>
+ struct ComparisonPolicy;
+#endif
+
+ /**
+ * ComparisonPolicy: specialisation for NoComparisonTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_, typename ComparisonMethodTag_>
+ class ComparisonPolicy<OurType_, comparison_mode::NoComparisonTag, ComparisonMethodTag_>
+ {
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::NoComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef ComparisonMethodTag_ ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+ };
+
+ /**
+ * ComparisonPolicy: specialisation for EqualityComparisonTag +
+ * CompareByMemberTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_, typename MemberType_>
+ class ComparisonPolicy<OurType_, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<MemberType_> >
+ {
+ private:
+ const MemberType_ OurType_::* const _v;
+
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::EqualityComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef comparison_method::CompareByMemberTag<MemberType_> ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+
+ /// Constructor.
+ ComparisonPolicy(const MemberType_ OurType_::* const v) :
+ _v(v)
+ {
+ }
+
+ /// Copy constructor.
+ ComparisonPolicy(const ComparisonPolicy & other) :
+ _v(other._v)
+ {
+ }
+
+define(`make_operator', `
+ /// $1 operator.
+ bool operator$1 (const OurType_ & other) const
+ {
+ return static_cast<const OurType_ *>(this)->*_v $1 other.*(
+ (static_cast<const ComparisonPolicyType *>(&other))->_v);
+ }
+')
+make_operator(`==')
+make_operator(`!=')
+
+ };
+
+ /**
+ * ComparisonPolicy: specialisation for EqualityComparisonTag +
+ * CompareByMemberComparisonFunctionTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_>
+ class ComparisonPolicy<OurType_, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>
+ {
+ private:
+ bool (OurType_::* const _v)(const OurType_ &) const;
+
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::EqualityComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef comparison_method::CompareByMemberComparisonFunctionTag ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+
+ /// Constructor.
+ ComparisonPolicy(bool (OurType_::* const v)(const OurType_ &) const) :
+ _v(v)
+ {
+ }
+
+ /// Copy constructor.
+ ComparisonPolicy(const ComparisonPolicy & other) :
+ _v(other._v)
+ {
+ }
+
+ /// Equal operator.
+ bool operator== (const OurType_ & other) const
+ {
+ return (static_cast<const OurType_ *>(this)->*_v)(other);
+ }
+
+ /// Not equal operator.
+ bool operator!= (const OurType_ & other) const
+ {
+ return ! (static_cast<const OurType_ *>(this)->*_v)(other);
+ }
+ };
+
+ /**
+ * ComparisonPolicy: specialisation for FullComparisonTag +
+ * CompareByMemberTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_, typename MemberType_>
+ class ComparisonPolicy<OurType_, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<MemberType_> >
+ {
+ private:
+ const MemberType_ OurType_::* const _v;
+
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::FullComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef comparison_method::CompareByMemberTag<MemberType_> ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+
+ /// Constructor.
+ ComparisonPolicy(const MemberType_ OurType_::* const v) :
+ _v(v)
+ {
+ }
+
+ /// Copy constructor.
+ ComparisonPolicy(const ComparisonPolicy & other) :
+ _v(other._v)
+ {
+ }
+
+define(`make_operator', `
+ /// $1 operator.
+ bool operator$1 (const OurType_ & other) const
+ {
+ return static_cast<const OurType_ *>(this)->*_v $1 other.*(
+ (static_cast<const ComparisonPolicyType *>(&other))->_v);
+ }
+')
+make_operator(`==')
+make_operator(`!=')
+make_operator(`<=')
+make_operator(`>=')
+make_operator(`<')
+make_operator(`>')
+
+ };
+
+ /**
+ * ComparisonPolicy: specialisation for FullComparisonTag +
+ * CompareByMemberComparisonFunctionTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_>
+ class ComparisonPolicy<OurType_, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>
+ {
+ private:
+ int (OurType_::* const _v)(const OurType_ &) const;
+
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::FullComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef comparison_method::CompareByMemberComparisonFunctionTag ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+
+ /// Constructor.
+ ComparisonPolicy(int (OurType_::* v)(const OurType_ &) const) :
+ _v(v)
+ {
+ }
+
+ /// Copy constructor.
+ ComparisonPolicy(const ComparisonPolicy & other) :
+ _v(other._v)
+ {
+ }
+
+define(`make_operator', `
+ /// $1 operator.
+ bool operator$1 (const OurType_ & other) const
+ {
+ return (static_cast<const OurType_ *>(this)->*_v)(other) $1 0;
+ }
+')
+make_operator(`==')
+make_operator(`!=')
+make_operator(`<=')
+make_operator(`>=')
+make_operator(`<')
+make_operator(`>')
+
+ };
+
+ /**
+ * ComparisonPolicy: specialisation for EqualityComparisonTag +
+ * CompareByMemberFetchFunctionTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_, typename MemberType_>
+ class ComparisonPolicy<OurType_, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberFetchFunctionTag<MemberType_> >
+ {
+ private:
+ MemberType_ (OurType_::* const _v)() const;
+
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::EqualityComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef comparison_method::CompareByMemberFetchFunctionTag<MemberType_> ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+
+ /// Constructor.
+ ComparisonPolicy(MemberType_ (OurType_::* const v)() const) :
+ _v(v)
+ {
+ }
+
+ /// Copy constructor.
+ ComparisonPolicy(const ComparisonPolicy & other) :
+ _v(other._v)
+ {
+ }
+
+define(`make_operator', `
+ /// $1 operator.
+ bool operator$1 (const OurType_ & other) const
+ {
+ return (static_cast<const OurType_ *>(this)->*_v)() $1 (other.*other._v)();
+ }
+')
+make_operator(`==')
+make_operator(`!=')
+ };
+
+ /**
+ * ComparisonPolicy: specialisation for FullComparisonTag +
+ * CompareByMemberFetchFunctionTag.
+ *
+ * \ingroup grpcompare
+ */
+ template <typename OurType_, typename MemberType_>
+ class ComparisonPolicy<OurType_, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberFetchFunctionTag<MemberType_> >
+ {
+ private:
+ MemberType_ (OurType_::* const _v)() const;
+
+ public:
+ /// Our comparison mode.
+ typedef comparison_mode::FullComparisonTag ComparisonPolicyModeTag;
+
+ /// Our comparison method.
+ typedef comparison_method::CompareByMemberFetchFunctionTag<MemberType_> ComparisonPolicyMethodTag;
+
+ /// Our comparison policy.
+ typedef ComparisonPolicy<OurType_, ComparisonPolicyModeTag, ComparisonPolicyMethodTag> ComparisonPolicyType;
+
+ /// Constructor.
+ ComparisonPolicy(MemberType_ (OurType_::* const v)() const) :
+ _v(v)
+ {
+ }
+
+ /// Copy constructor.
+ ComparisonPolicy(const ComparisonPolicy & other) :
+ _v(other._v)
+ {
+ }
+
+define(`make_operator', `
+ /// $1 operator.
+ bool operator$1 (const OurType_ & other) const
+ {
+ return ((static_cast<const OurType_ *>(this)->*_v)()) $1
+ ((other.*(static_cast<const OurType_ *>(&other)->_v))());
+ }
+')
+make_operator(`==')
+make_operator(`!=')
+make_operator(`>=')
+make_operator(`<=')
+make_operator(`>')
+make_operator(`<')
+
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/comparison_policy_TEST.cc b/0.4.0/paludis/util/comparison_policy_TEST.cc
new file mode 100644
index 000000000..62daae004
--- /dev/null
+++ b/0.4.0/paludis/util/comparison_policy_TEST.cc
@@ -0,0 +1,44 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/comparison_policy.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+/// \todo
+
+namespace test_cases
+{
+ /**
+ * Test comparisons.
+ *
+ * \ingroup grptestcases
+ */
+ struct ComparisonTest : TestCase
+ {
+ ComparisonTest() : TestCase("comparison") { }
+
+ void run()
+ {
+ }
+ } test_comparison;
+}
diff --git a/0.4.0/paludis/util/composite_pattern.hh b/0.4.0/paludis/util/composite_pattern.hh
new file mode 100644
index 000000000..215bfb386
--- /dev/null
+++ b/0.4.0/paludis/util/composite_pattern.hh
@@ -0,0 +1,66 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_COMPOSITE_PATTERN_HH
+#define PALUDIS_GUARD_PALUDIS_COMPOSITE_PATTERN_HH 1
+
+/** \file
+ * Declarations for the Composite template class.
+ *
+ * \ingroup grpcomposite
+ */
+
+namespace paludis
+{
+ /**
+ * A Composite class represents both a class and a collection of
+ * child instances of the same class.
+ *
+ * \ingroup grpcomposite
+ */
+ template <typename ChildClass_, typename CompositeClass_ = ChildClass_>
+ class Composite
+ {
+ private:
+ mutable CompositeClass_ * _composite;
+
+ protected:
+ /**
+ * Constructor.
+ */
+ Composite() :
+ _composite(0)
+ {
+ }
+
+ public:
+ /**
+ * Fetch a CompositeClass_ representation of ourself, or 0 if we
+ * are not composite.
+ */
+ CompositeClass_ * get_composite() const
+ {
+ if (0 == _composite)
+ _composite = & dynamic_cast<CompositeClass_>(*this);
+ return _composite;
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/container_entry.hh b/0.4.0/paludis/util/container_entry.hh
new file mode 100644
index 000000000..737033af3
--- /dev/null
+++ b/0.4.0/paludis/util/container_entry.hh
@@ -0,0 +1,77 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_CONTAINER_ENTRY_HH
+#define PALUDIS_GUARD_PALUDIS_CONTAINER_ENTRY_HH 1
+
+#include <list>
+
+/** \file
+ * Declarations for the ContainerEntry class.
+ *
+ * \ingroup grpcontainerentries
+ */
+
+namespace paludis
+{
+ /**
+ * Hold an entry in a container for as long as our ContainerEntry instance
+ * is in scope (RAII, see \ref EffCpp item 13 or \ref TCppPL section 14.4).
+ *
+ * \ingroup grpcontainerentries
+ */
+ template <typename Container_>
+ struct ContainerEntry;
+
+ /**
+ * Hold an entry in a container for as long as our ContainerEntry instance
+ * is in scope (RAII, see \ref EffCpp item 13 or \ref TCppPL section 14.4;
+ * partial specialisation for std::list).
+ *
+ * \ingroup grpcontainerentries
+ */
+ template <typename Item_>
+ class ContainerEntry<std::list<Item_> >
+ {
+ private:
+ std::list<Item_> * const _list;
+
+ typename std::list<Item_>::iterator _item;
+
+ public:
+ /**
+ * Constructor.
+ */
+ ContainerEntry(std::list<Item_> * const list, const Item_ & item) :
+ _list(list),
+ _item(list->insert(list->begin(), item))
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ ~ContainerEntry()
+ {
+ _list->erase(_item);
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/container_entry_TEST.cc b/0.4.0/paludis/util/container_entry_TEST.cc
new file mode 100644
index 000000000..2daf9d76a
--- /dev/null
+++ b/0.4.0/paludis/util/container_entry_TEST.cc
@@ -0,0 +1,65 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <algorithm>
+#include <paludis/util/container_entry.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ /**
+ * \test Test ContainerEntry on a list.
+ *
+ * \ingroup Test
+ */
+ struct ContainerEntryListTest : TestCase
+ {
+ ContainerEntryListTest() : TestCase("list") { }
+
+ void run()
+ {
+ std::list<int> list;
+ TEST_CHECK(list.empty());
+ {
+ ContainerEntry<std::list<int> > e1(&list, 5);
+ TEST_CHECK(list.begin() != list.end());
+ TEST_CHECK_EQUAL(std::distance(list.begin(), list.end()), 1);
+ TEST_CHECK(list.end() != std::find(list.begin(), list.end(), 5));
+
+ {
+ ContainerEntry<std::list<int> > e2(&list, 4);
+ TEST_CHECK(list.begin() != list.end());
+ TEST_CHECK_EQUAL(std::distance(list.begin(), list.end()), 2);
+ TEST_CHECK(list.end() != std::find(list.begin(), list.end(), 5));
+ TEST_CHECK(list.end() != std::find(list.begin(), list.end(), 4));
+ }
+
+ TEST_CHECK(list.begin() != list.end());
+ TEST_CHECK_EQUAL(std::distance(list.begin(), list.end()), 1);
+ TEST_CHECK(list.end() != std::find(list.begin(), list.end(), 5));
+ }
+ TEST_CHECK(list.empty());
+ }
+ } test_container_entry_list;
+}
+
diff --git a/0.4.0/paludis/util/counted_ptr.cc b/0.4.0/paludis/util/counted_ptr.cc
new file mode 100644
index 000000000..3d13a071b
--- /dev/null
+++ b/0.4.0/paludis/util/counted_ptr.cc
@@ -0,0 +1,28 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/counted_ptr.hh>
+
+using namespace paludis;
+
+CountedPtrError::CountedPtrError() throw () :
+ Exception("CountedPtr dereference error")
+{
+}
+
diff --git a/0.4.0/paludis/util/counted_ptr.hh b/0.4.0/paludis/util/counted_ptr.hh
new file mode 100644
index 000000000..177d1b886
--- /dev/null
+++ b/0.4.0/paludis/util/counted_ptr.hh
@@ -0,0 +1,606 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_COUNTED_PTR_HH
+#define PALUDIS_GUARD_PALUDIS_COUNTED_PTR_HH 1
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/comparison_policy.hh>
+#include <paludis/util/exception.hh>
+
+/** \file
+ * Declaration for the CountedPtr template class.
+ *
+ * \ingroup grppointers
+ */
+
+namespace paludis
+{
+ /**
+ * Thrown when a CountedPtr check fails.
+ *
+ * \ingroup grppointers
+ * \ingroup grpexceptions
+ */
+ class CountedPtrError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ CountedPtrError() throw ();
+ };
+
+ /**
+ * Contains CountedPtr count policies.
+ *
+ * \ingroup grppointers
+ */
+ namespace count_policy
+ {
+ /**
+ * CountedPtr policy: reference counts are stored separately.
+ *
+ * \ingroup grppointers
+ */
+ struct ExternalCountTag
+ {
+ };
+
+ /**
+ * CountedPtr policy: reference counts are stored by the class via the
+ * Counted subclass.
+ *
+ * \ingroup grppointers
+ */
+ struct InternalCountTag
+ {
+ };
+ }
+
+ /**
+ * Contains CountedPtr dereference policies.
+ *
+ * \ingroup grppointers
+ */
+ namespace dereference_policy
+ {
+ /**
+ * CountedPtr dereferences are not checked.
+ *
+ * \ingroup grppointers
+ */
+ struct UncheckedDereferenceTag
+ {
+ };
+
+ /**
+ * CountedPtr dereferences are checked, and a CountedPtrError is
+ * thrown for 0 dereferences.
+ *
+ * \ingroup grppointers
+ */
+ struct CheckedDereferenceTag
+ {
+ };
+ }
+
+ /**
+ * CountedPtr internals.
+ *
+ * \ingroup grppointers
+ */
+ namespace counted_ptr_internals
+ {
+ /**
+ * Base class for CountedPtr.
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_, typename DereferencePolicy_>
+ class CountedPtrBase;
+
+ /**
+ * Base class for CountedPtr (specialisation for UncheckedDereferenceTag).
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_>
+ class CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag> :
+ public ComparisonPolicy<CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag>,
+ comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<T_ *> >
+ {
+ private:
+ CountedPtrBase(const CountedPtrBase & other);
+
+ const CountedPtrBase & operator= (const CountedPtrBase & other);
+
+ protected:
+ /**
+ * Pointer to our data.
+ */
+ T_ * _ptr;
+
+ /**
+ * Constructor.
+ */
+ CountedPtrBase(T_ * ptr) :
+ ComparisonPolicy<CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag>,
+ comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<T_ *> >(
+ &CountedPtrBase::_ptr),
+ _ptr(ptr)
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ ~CountedPtrBase()
+ {
+ }
+
+ public:
+ /**
+ * Dereference operator (const).
+ */
+ inline const T_ & operator* () const;
+
+ /**
+ * Dereference to member operator (const).
+ */
+ inline const T_ * operator-> () const;
+
+ /**
+ * Dereference operator (non const).
+ */
+ T_ & operator* ();
+
+ /**
+ * Dereference to member operator (non const).
+ */
+ T_ * operator-> ();
+
+ /**
+ * Not null?
+ */
+ operator bool() const
+ {
+ return _ptr;
+ }
+
+ /**
+ * Fetch our raw pointer.
+ */
+ T_ * raw_pointer() const
+ {
+ return _ptr;
+ }
+ };
+
+ template <typename T_>
+ const T_ & CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag>::operator* () const
+ {
+ return *_ptr;
+ }
+
+ template <typename T_>
+ const T_ * CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag>::operator-> () const
+ {
+ return _ptr;
+ }
+
+ template <typename T_>
+ T_ & CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag>::operator* ()
+ {
+ return *_ptr;
+ }
+
+ template <typename T_>
+ T_ * CountedPtrBase<T_, dereference_policy::UncheckedDereferenceTag>::operator-> ()
+ {
+ return _ptr;
+ }
+ /**
+ * Base class for CountedPtr (specialisation for CheckedDereferenceTag).
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_>
+ class CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag> :
+ public ComparisonPolicy<CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag>,
+ comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<T_ *> >
+ {
+ private:
+ CountedPtrBase(const CountedPtrBase & other);
+
+ const CountedPtrBase & operator= (const CountedPtrBase & other);
+
+ protected:
+ /**
+ * Pointer to our data.
+ */
+ T_ * _ptr;
+
+ /**
+ * Constructor.
+ */
+ CountedPtrBase(T_ * ptr) :
+ ComparisonPolicy<CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag>,
+ comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<T_ *> >(
+ &CountedPtrBase::_ptr),
+ _ptr(ptr)
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ ~CountedPtrBase()
+ {
+ }
+
+ public:
+ /**
+ * Dereference operator (const).
+ */
+ inline const T_ & operator* () const;
+
+ /**
+ * Dereference to member operator (const).
+ */
+ inline const T_ * operator-> () const;
+
+ /**
+ * Dereference operator (non const).
+ */
+ T_ & operator* ();
+
+ /**
+ * Dereference to member operator (non const).
+ */
+ T_ * operator-> ();
+
+ /**
+ * Not null?
+ */
+ operator bool() const
+ {
+ return _ptr;
+ }
+
+ /**
+ * Fetch our raw pointer.
+ */
+ T_ * raw_pointer() const
+ {
+ return _ptr;
+ }
+ };
+
+ template <typename T_>
+ const T_ & CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag>::operator* () const
+ {
+ if (0 == _ptr)
+ throw CountedPtrError();
+ return *_ptr;
+ }
+
+ template <typename T_>
+ const T_ * CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag>::operator-> () const
+ {
+ if (0 == _ptr)
+ throw CountedPtrError();
+ return _ptr;
+ }
+
+ template <typename T_>
+ T_ & CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag>::operator* ()
+ {
+ if (0 == _ptr)
+ throw CountedPtrError();
+ return *_ptr;
+ }
+
+ template <typename T_>
+ T_ * CountedPtrBase<T_, dereference_policy::CheckedDereferenceTag>::operator-> ()
+ {
+ if (0 == _ptr)
+ throw CountedPtrError();
+ return _ptr;
+ }
+ }
+
+ /**
+ * Reference counted pointer class.
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_, typename CountPolicy_ = count_policy::InternalCountTag,
+ typename DereferencePolicy_ = dereference_policy::UncheckedDereferenceTag>
+ class CountedPtr;
+
+ /**
+ * Base for an internal counted class.
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_, typename DereferencePolicy_ = dereference_policy::UncheckedDereferenceTag>
+ class InternalCounted;
+
+ /**
+ * Reference counted pointer class (specialisation for ExternalCountTag).
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_, typename DereferencePolicy_>
+ class CountedPtr<T_, count_policy::ExternalCountTag, DereferencePolicy_> :
+ public counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>
+ {
+ private:
+ unsigned * _ref_count;
+
+ public:
+ /**
+ * Constructor, from a raw pointer.
+ */
+ explicit CountedPtr(T_ * const ptr) :
+ counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>(ptr),
+ _ref_count(new unsigned(1))
+ {
+ }
+
+ /**
+ * Constructor, from another CountedPtr.
+ */
+ CountedPtr(const CountedPtr & other) :
+ counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>(other.raw_pointer()),
+ _ref_count(other._ref_count)
+ {
+ ++*_ref_count;
+ }
+
+ /**
+ * Constructor, from another CountedPtr of a descendent of our
+ * data's class.
+ */
+ template <typename O_>
+ CountedPtr(const CountedPtr<O_, count_policy::ExternalCountTag> & other) :
+ counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>(other._ptr),
+ _ref_count(other.reference_count_pointer())
+ {
+ ++*_ref_count;
+ }
+
+ /**
+ * Destructor.
+ */
+ ~CountedPtr()
+ {
+ if (0 == --(*_ref_count))
+ {
+ delete this->_ptr;
+ delete _ref_count;
+ }
+ }
+
+ /**
+ * Assignment, from another CountedPtr.
+ */
+ const CountedPtr & operator= (const CountedPtr & other)
+ {
+ if (other._ptr != this->_ptr)
+ {
+ if (0 == --*_ref_count)
+ {
+ delete this->_ptr;
+ delete _ref_count;
+ }
+
+ this->_ptr = other._ptr;
+ _ref_count = other._ref_count;
+ ++*_ref_count;
+ }
+ return *this;
+ }
+
+ /**
+ * Explicit assignment, from a raw pointer.
+ */
+ const CountedPtr & assign(T_ * const other)
+ {
+ return operator= (CountedPtr<T_, count_policy::ExternalCountTag>(other));
+ }
+
+ /**
+ * Explicit assignment to zero.
+ */
+ const CountedPtr & zero()
+ {
+ return operator= (CountedPtr<T_, count_policy::ExternalCountTag>(0));
+ }
+
+ /**
+ * Fetch our reference count pointer.
+ */
+ unsigned * reference_count_pointer() const
+ {
+ return _ref_count;
+ }
+ };
+
+ /**
+ * Reference counted pointer class (specialisation for InternalCountTag).
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_, typename DereferencePolicy_>
+ class CountedPtr<T_, count_policy::InternalCountTag, DereferencePolicy_> :
+ public counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>
+ {
+ public:
+ /**
+ * Constructor, from a raw pointer.
+ */
+ explicit CountedPtr(T_ * const ptr) :
+ counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>(ptr)
+ {
+ if (0 != this->_ptr)
+ ++*this->_ptr->reference_count_pointer();
+ }
+
+ /**
+ * Constructor, from another CountedPtr.
+ */
+ CountedPtr(const CountedPtr & other) :
+ counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>(other._ptr)
+ {
+ if (0 != this->_ptr)
+ ++*this->_ptr->reference_count_pointer();
+ }
+
+ /**
+ * Constructor, from another CountedPtr of a descendent of our
+ * data's class.
+ */
+ template <typename O_>
+ CountedPtr(const CountedPtr<O_, count_policy::InternalCountTag> & other) :
+ counted_ptr_internals::CountedPtrBase<T_, DereferencePolicy_>(
+ static_cast<T_ *>(other.raw_pointer()))
+ {
+ if (0 != this->_ptr)
+ ++*this->_ptr->reference_count_pointer();
+ }
+
+ /**
+ * Destructor.
+ */
+ ~CountedPtr()
+ {
+ if (0 != this->_ptr)
+ if (0 == --(*this->_ptr->reference_count_pointer()))
+ delete this->_ptr;
+ }
+
+ /**
+ * Assignment, from another CountedPtr.
+ */
+ const CountedPtr & operator= (const CountedPtr & other)
+ {
+ if (other._ptr != this->_ptr)
+ {
+ if (0 != this->_ptr)
+ if (0 == --(*this->_ptr->reference_count_pointer()))
+ delete this->_ptr;
+
+ this->_ptr = other._ptr;
+ if (0 != this->_ptr)
+ ++*this->_ptr->reference_count_pointer();
+ }
+ return *this;
+ }
+
+ /**
+ * Explicit assignment, from a raw pointer.
+ */
+ const CountedPtr & assign(T_ * const other)
+ {
+ return operator= (CountedPtr<T_, count_policy::InternalCountTag>(other));
+ }
+
+ /**
+ * Explicit assignment to zero.
+ */
+ const CountedPtr & zero()
+ {
+ return operator= (CountedPtr<T_, count_policy::InternalCountTag>(0));
+ }
+
+ /**
+ * Fetch our reference count pointer.
+ */
+ unsigned * reference_count_pointer() const
+ {
+ if (0 != this->_ptr)
+ return this->_ptr->reference_count_pointer();
+ else
+ return 0;
+ }
+ };
+}
+
+#include <paludis/util/instantiation_policy.hh>
+
+namespace paludis
+{
+ /**
+ * Base class for an internally counted class.
+ *
+ * \ingroup grppointers
+ */
+ template <typename T_, typename DereferencePolicy_>
+ class InternalCounted :
+ private InstantiationPolicy<InternalCounted<T_, DereferencePolicy_>,
+ instantiation_method::NonCopyableTag>
+ {
+ private:
+ mutable unsigned _ref_count;
+
+ protected:
+ /**
+ * Constructor.
+ */
+ InternalCounted() :
+ _ref_count(0)
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ ~InternalCounted()
+ {
+ }
+
+ public:
+ /**
+ * A CountedPtr to us.
+ */
+ typedef CountedPtr<T_, count_policy::InternalCountTag, DereferencePolicy_> Pointer;
+
+ /**
+ * A CountedPtr to us (const).
+ */
+ typedef CountedPtr<const T_, count_policy::InternalCountTag, DereferencePolicy_> ConstPointer;
+
+ /**
+ * Fetch a pointer to our reference count (may be zero).
+ */
+ unsigned * reference_count_pointer() const
+ {
+ return & _ref_count;
+ }
+ };
+}
+
+#endif
+
diff --git a/0.4.0/paludis/util/counted_ptr_TEST.cc b/0.4.0/paludis/util/counted_ptr_TEST.cc
new file mode 100644
index 000000000..2a2ba9431
--- /dev/null
+++ b/0.4.0/paludis/util/counted_ptr_TEST.cc
@@ -0,0 +1,390 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/counted_ptr.hh>
+#include <string>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for counted_ptr.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace
+{
+ /**
+ * Test InternalCounted class.
+ *
+ * \ingroup grptestcases
+ */
+ class MyClass :
+ public InternalCounted<MyClass>
+ {
+ private:
+ int _v;
+
+ public:
+ MyClass(const int v) :
+ _v(v)
+ {
+ }
+
+ MyClass(const MyClass & other) :
+ InternalCounted<MyClass>(),
+ _v(other._v)
+ {
+ }
+
+ const MyClass & operator= (const MyClass & other)
+ {
+ _v = other._v;
+ return *this;
+ }
+
+ bool operator== (const MyClass & other) const
+ {
+ return _v == other._v;
+ }
+
+ int value() const
+ {
+ return _v;
+ }
+ };
+}
+
+/**
+ * Test InternalCounted class is stringifiable.
+ *
+ * \ingroup grptestcases
+ */
+std::ostream & operator<< (std::ostream & s, const MyClass & c)
+{
+ s << c.value();
+ return s;
+}
+
+namespace test_cases
+{
+ /**
+ * \test CountedPtr creation tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrCreationTests : TestCase
+ {
+ CountedPtrCreationTests() : TestCase("CountedPtr creation tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag> i(new int(10));
+ CountedPtr<std::string, count_policy::ExternalCountTag> j(new std::string("moo"));
+ }
+ } test_counted_ptr_creation;
+
+ /**
+ * \test CountedPtr dereference tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrDereferenceTests : TestCase
+ {
+ CountedPtrDereferenceTests() : TestCase("CountedPtr dereference tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag> i(new int(10));
+ TEST_CHECK_EQUAL(*i, 10);
+
+ CountedPtr<std::string, count_policy::ExternalCountTag> j(new std::string("moo"));
+ TEST_CHECK_EQUAL(*j, "moo");
+ TEST_CHECK_EQUAL(j->length(), 3);
+ }
+ } test_counted_ptr_dereference;
+
+ /**
+ * \test CountedPtr copy tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrCopyTests : TestCase
+ {
+ CountedPtrCopyTests() : TestCase("CountedPtr copy tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag> i(new int(10));
+ TEST_CHECK_EQUAL(*i, 10);
+
+ CountedPtr<int, count_policy::ExternalCountTag> i2(i);
+ TEST_CHECK_EQUAL(*i, 10);
+ TEST_CHECK_EQUAL(*i2, 10);
+ }
+ } test_counted_ptr_copy;
+
+ /**
+ * \test CountedPtr dereference-assign tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrDereferenceAssignTests : TestCase
+ {
+ CountedPtrDereferenceAssignTests() : TestCase("CountedPtr dereference assign tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag> i(new int(10));
+ TEST_CHECK_EQUAL(*i, 10);
+ *i = 20;
+ TEST_CHECK_EQUAL(*i, 20);
+
+ CountedPtr<int, count_policy::ExternalCountTag> i2(i);
+ TEST_CHECK_EQUAL(*i, 20);
+ TEST_CHECK_EQUAL(*i2, 20);
+
+ *i = 30;
+ TEST_CHECK_EQUAL(*i, 30);
+ TEST_CHECK_EQUAL(*i2, 30);
+
+ *i2 = 40;
+ TEST_CHECK_EQUAL(*i, 40);
+ TEST_CHECK_EQUAL(*i2, 40);
+ }
+ } test_counted_ptr_dereference_assign;
+
+ /**
+ * \test CountedPtr assign value tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrAssignValueTests : TestCase
+ {
+ CountedPtrAssignValueTests() : TestCase("CountedPtr assign value tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag> i(new int(10));
+ TEST_CHECK_EQUAL(*i, 10);
+ CountedPtr<int, count_policy::ExternalCountTag> i2(i);
+ TEST_CHECK_EQUAL(*i, 10);
+ TEST_CHECK_EQUAL(*i2, 10);
+
+ i = CountedPtr<int, count_policy::ExternalCountTag>(new int(20));
+ TEST_CHECK_EQUAL(*i, 20);
+ TEST_CHECK_EQUAL(*i2, 10);
+ }
+ } test_counted_ptr_assign_value;
+
+ /**
+ * \test CountedPtr assign pointer tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrAssignPointerTests : TestCase
+ {
+ CountedPtrAssignPointerTests() : TestCase("CountedPtr assign pointer tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag> i(new int(10));
+ TEST_CHECK_EQUAL(*i, 10);
+ CountedPtr<int, count_policy::ExternalCountTag> i2(i);
+ TEST_CHECK_EQUAL(*i, 10);
+ TEST_CHECK_EQUAL(*i2, 10);
+
+ CountedPtr<int, count_policy::ExternalCountTag> i3(new int(30));
+
+ i = i3;
+ TEST_CHECK_EQUAL(*i, 30);
+ TEST_CHECK_EQUAL(*i2, 10);
+ TEST_CHECK_EQUAL(*i3, 30);
+
+ i.assign(new int(50));
+ TEST_CHECK_EQUAL(*i, 50);
+
+ i.zero();
+ TEST_CHECK(! i);
+ }
+ } test_counted_ptr_assign_pointer;
+
+ /**
+ * \test CountedPtr internal creation tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrInternalCreationTests : TestCase
+ {
+ CountedPtrInternalCreationTests() : TestCase("CountedPtr internal creation tests") { }
+
+ void run()
+ {
+ MyClass::Pointer i(new MyClass(10));
+ }
+ } test_counted_ptr_internal_creation;
+
+ /**
+ * \test CountedPtr internal dereference tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrInternalDereferenceTests : TestCase
+ {
+ CountedPtrInternalDereferenceTests() : TestCase("CountedPtr internal dereference tests") { }
+
+ void run()
+ {
+ MyClass::Pointer i(new MyClass(10));
+ TEST_CHECK_EQUAL(*i, 10);
+
+ MyClass::Pointer j(new MyClass(20));
+ TEST_CHECK_EQUAL(*j, 20);
+ }
+ } test_counted_ptr_internal_dereference;
+
+ /**
+ * \test CountedPtr internal copy tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrInternalCopyTests : TestCase
+ {
+ CountedPtrInternalCopyTests() : TestCase("CountedPtr internal copy tests") { }
+
+ void run()
+ {
+ MyClass::Pointer i(new MyClass(10));
+ TEST_CHECK_EQUAL(*i, 10);
+
+ MyClass::Pointer i2(i);
+ TEST_CHECK_EQUAL(*i, 10);
+ TEST_CHECK_EQUAL(*i2, 10);
+ }
+ } test_counted_ptr_internal_copy;
+
+ /**
+ * \test CountedPtr internal dereference-assign tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrInternalDereferenceAssignTests : TestCase
+ {
+ CountedPtrInternalDereferenceAssignTests() :
+ TestCase("CountedPtr internal dereference assign tests") { }
+
+ void run()
+ {
+ MyClass::Pointer i(new MyClass(10));
+ TEST_CHECK_EQUAL(*i, 10);
+ *i = 20;
+ TEST_CHECK_EQUAL(*i, 20);
+
+ MyClass::Pointer i2(i);
+ TEST_CHECK_EQUAL(*i, 20);
+ TEST_CHECK_EQUAL(*i2, 20);
+
+ *i = 30;
+ TEST_CHECK_EQUAL(*i, 30);
+ TEST_CHECK_EQUAL(*i2, 30);
+
+ *i2 = 40;
+ TEST_CHECK_EQUAL(*i, 40);
+ TEST_CHECK_EQUAL(*i2, 40);
+ }
+ } test_counted_ptr_internal_dereference_assign;
+
+ /**
+ * \test CountedPtr internal assign value tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrInternalAssignValueTests : TestCase
+ {
+ CountedPtrInternalAssignValueTests() :
+ TestCase("CountedPtr internal assign value tests") { }
+
+ void run()
+ {
+ MyClass::Pointer i(new MyClass(10));
+ TEST_CHECK_EQUAL(*i, 10);
+ MyClass::Pointer i2(i);
+ TEST_CHECK_EQUAL(*i, 10);
+ TEST_CHECK_EQUAL(*i2, 10);
+
+ i = MyClass::Pointer(new MyClass(20));
+ TEST_CHECK_EQUAL(*i, 20);
+ TEST_CHECK_EQUAL(*i2, 10);
+ }
+ } test_counted_ptr_internal_assign_value;
+
+ /**
+ * \test CountedPtr internal assign pointer tests.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrInternalAssignPointerTests : TestCase
+ {
+ CountedPtrInternalAssignPointerTests() :
+ TestCase("CountedPtr internal assign pointer tests") { }
+
+ void run()
+ {
+ MyClass::Pointer i(new MyClass(10));
+ TEST_CHECK_EQUAL(*i, 10);
+ MyClass::Pointer i2(i);
+ TEST_CHECK_EQUAL(*i, 10);
+ TEST_CHECK_EQUAL(*i2, 10);
+
+ MyClass::Pointer i3(new MyClass(30));
+
+ i = i3;
+ TEST_CHECK_EQUAL(*i, 30);
+ TEST_CHECK_EQUAL(*i2, 10);
+ TEST_CHECK_EQUAL(*i3, 30);
+
+ i.assign(new MyClass(50));
+ TEST_CHECK_EQUAL(*i, 50);
+
+ i.zero();
+ TEST_CHECK(! i);
+ }
+ } test_counted_ptr_internal_assign_pointer;
+
+ /**
+ * \test CountedPtr zero dereferences.
+ *
+ * \ingroup grptestcases
+ */
+ struct CountedPtrZeroDeferenceTests : TestCase
+ {
+ CountedPtrZeroDeferenceTests() :
+ TestCase("CountedPtr zero dereference tests") { }
+
+ void run()
+ {
+ CountedPtr<int, count_policy::ExternalCountTag, dereference_policy::CheckedDereferenceTag> i(0);
+ TEST_CHECK_THROWS(++*i, CountedPtrError);
+ }
+ } test_counted_ptr_zero_dereference;
+}
+
diff --git a/0.4.0/paludis/util/deleter.cc b/0.4.0/paludis/util/deleter.cc
new file mode 100644
index 000000000..76e7ff7ae
--- /dev/null
+++ b/0.4.0/paludis/util/deleter.cc
@@ -0,0 +1,32 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/deleter.hh>
+
+/** \file
+ * Implementation for Deleter.
+ *
+ * \ingroup grpdeleter
+ */
+
+using namespace paludis;
+
+Deleter::Deleter()
+{
+}
diff --git a/0.4.0/paludis/util/deleter.hh b/0.4.0/paludis/util/deleter.hh
new file mode 100644
index 000000000..8ae9bee71
--- /dev/null
+++ b/0.4.0/paludis/util/deleter.hh
@@ -0,0 +1,57 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_DELETER_HH
+#define PALUDIS_GUARD_PALUDIS_DELETER_HH 1
+
+/** \file
+ * Declarations for the Deleter class.
+ *
+ * \ingroup grpdeleter
+ */
+
+namespace paludis
+{
+ /**
+ * A Deleter is a functor that deletes something.
+ *
+ * See \ref EffSTL item 7.
+ *
+ * \ingroup grpdeleter
+ */
+ class Deleter
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ Deleter();
+
+ /**
+ * Delete an item.
+ */
+ template <typename T_>
+ void operator() (T_ t)
+ {
+ delete t;
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/deleter_TEST.cc b/0.4.0/paludis/util/deleter_TEST.cc
new file mode 100644
index 000000000..05b6006d0
--- /dev/null
+++ b/0.4.0/paludis/util/deleter_TEST.cc
@@ -0,0 +1,85 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <algorithm>
+#include <paludis/util/deleter.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for deleter.hh.
+ *
+ * \ingroup grptestcases
+ */
+
+namespace
+{
+ /**
+ * Test class to be deleted, with an instance count.
+ *
+ * \ingroup grptestcases
+ */
+ struct MyClass
+ {
+ static int instances;
+
+ MyClass()
+ {
+ ++instances;
+ }
+
+ ~MyClass()
+ {
+ --instances;
+ }
+ };
+}
+
+int MyClass::instances = 0;
+
+namespace test_cases
+{
+ /**
+ * Test Deleter.
+ *
+ * \ingroup grptestcases
+ */
+ struct DeleterTest : TestCase
+ {
+ DeleterTest() : TestCase("deleter") { }
+
+ void run()
+ {
+ std::vector<MyClass *> v;
+ TEST_CHECK_EQUAL(MyClass::instances, 0);
+ v.push_back(new MyClass);
+ TEST_CHECK_EQUAL(MyClass::instances, 1);
+ v.push_back(new MyClass);
+ TEST_CHECK_EQUAL(MyClass::instances, 2);
+ v.push_back(new MyClass);
+ TEST_CHECK_EQUAL(MyClass::instances, 3);
+ std::for_each(v.begin(), v.end(), Deleter());
+ TEST_CHECK_EQUAL(MyClass::instances, 0);
+ }
+ } test_deleter;
+}
diff --git a/0.4.0/paludis/util/destringify.cc b/0.4.0/paludis/util/destringify.cc
new file mode 100644
index 000000000..f60126140
--- /dev/null
+++ b/0.4.0/paludis/util/destringify.cc
@@ -0,0 +1,34 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Stephen Bennett <spb@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <paludis/util/destringify.hh>
+
+/** \file
+ * Implementation for DestringifyError.
+ *
+ * \ingroup grpdestringify
+ */
+
+using namespace paludis;
+
+DestringifyError::DestringifyError(const std::string & str) throw () :
+ Exception("Couldn't destringify '" + str + "'." )
+{
+}
diff --git a/0.4.0/paludis/util/destringify.hh b/0.4.0/paludis/util/destringify.hh
new file mode 100644
index 000000000..1303de793
--- /dev/null
+++ b/0.4.0/paludis/util/destringify.hh
@@ -0,0 +1,149 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Stephen Bennett <spb@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_DESTRINGIFY_HH
+#define PALUDIS_GUARD_PALUDIS_DESTRINGIFY_HH 1
+
+#include <paludis/util/exception.hh>
+#include <sstream>
+#include <string>
+
+/** \file
+ * Destringify functions.
+ *
+ * \ingroup grpdestringify
+ */
+
+namespace paludis
+{
+ /**
+ * Default exception type thrown by destringify when it fails.
+ *
+ * \ingroup grpdestringify
+ * \ingroup grpexceptions
+ */
+ class DestringifyError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ DestringifyError(const std::string & str) throw ();
+ };
+
+ /**
+ * For internal use by destringify.
+ *
+ * \ingroup grpdestringify
+ */
+ namespace destringify_internals
+ {
+ /**
+ * Basic destringifier.
+ *
+ * \ingroup grpdestringify
+ */
+ template <typename Type_, typename Exception_>
+ struct Destringifier
+ {
+ /**
+ * Do the destringification.
+ */
+ static Type_ do_destringify(const std::string & s)
+ {
+ std::istringstream ss(s);
+ Type_ t;
+ ss >> t;
+ if (!ss.eof() || ss.bad())
+ throw Exception_(s);
+ return t;
+ }
+ };
+
+ /**
+ * Specialised destringify for std::string.
+ *
+ * \ingroup grpdestringify
+ */
+ template <typename Exception_>
+ struct Destringifier<std::string, Exception_>
+ {
+ /**
+ * Do the destringification.
+ */
+ static std::string do_destringify(const std::string & s)
+ {
+ return s;
+ }
+ };
+
+ /**
+ * Specialised destringify for bool.
+ *
+ * \ingroup grpdestringify
+ */
+ template <typename Exception_>
+ struct Destringifier<bool, Exception_>
+ {
+ /**
+ * Do the destringification.
+ */
+ static bool do_destringify(const std::string & s)
+ {
+ int i;
+ try
+ {
+ i = Destringifier<int, Exception_>::do_destringify(s);
+ return i > 0;
+ }
+ catch (DestringifyError &)
+ {
+ bool b;
+ std::istringstream ss(s);
+ ss >> std::boolalpha >> b;
+ if (! ss.eof() || ss.bad())
+ throw Exception_(s);
+ return b;
+ }
+ }
+ };
+ }
+
+ /**
+ * Extract a value of some type from a string.
+ *
+ * \ingroup grpdestringify
+ */
+ template <typename Type_, typename Exception_>
+ Type_ destringify(const std::string & s)
+ {
+ if (s == "")
+ throw Exception_("");
+
+ return destringify_internals::Destringifier<Type_, Exception_>::do_destringify(s);
+ }
+
+ template <typename Type_>
+ Type_ destringify(const std::string & s)
+ {
+ return destringify<Type_, DestringifyError>(s);
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/destringify_TEST.cc b/0.4.0/paludis/util/destringify_TEST.cc
new file mode 100644
index 000000000..8ea4cd647
--- /dev/null
+++ b/0.4.0/paludis/util/destringify_TEST.cc
@@ -0,0 +1,113 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Stephen Bennett <spb@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/destringify.hh>
+#include <string>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for destringify.hh
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /** \test
+ * Test destringify for integers.
+ *
+ * \ingroup grptestcases
+ */
+ struct DestringifyIntTests : TestCase
+ {
+ DestringifyIntTests() : TestCase("destringify int") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(destringify<int>("0"), 0);
+ TEST_CHECK_EQUAL(destringify<int>("1"), 1);
+ TEST_CHECK_EQUAL(destringify<int>("99"), 99);
+ TEST_CHECK_EQUAL(destringify<int>("-99"), -99);
+ TEST_CHECK_EQUAL(destringify<int>(" 12345"), 12345);
+ TEST_CHECK_THROWS(destringify<int>(""), DestringifyError);
+ }
+ } test_case_destringify_int;
+
+ /** \test
+ * Test destringify for floats.
+ *
+ * \ingroup grptestcases
+ */
+ struct DestringifyFloatTests : TestCase
+ {
+ DestringifyFloatTests() : TestCase("destringify float") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(destringify<float>("0"), 0.f);
+ TEST_CHECK_EQUAL(destringify<float>("0.0"), 0.f);
+ TEST_CHECK_EQUAL(destringify<float>("0.1"), 0.1f);
+ TEST_CHECK_EQUAL(destringify<float>("-1.54"), -1.54f);
+ TEST_CHECK_THROWS(destringify<float>("I am a fish"), DestringifyError);
+ }
+ } test_case_destringify_float;
+
+ /** \test
+ * Test destringify for strings.
+ *
+ * \ingroup grptestcases
+ */
+ struct DestringifyStringTests : TestCase
+ {
+ DestringifyStringTests() : TestCase("destringify string") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(destringify<std::string>("asdf"), "asdf");
+ TEST_CHECK_EQUAL(destringify<std::string>(" a f e b "), " a f e b ");
+ }
+ } test_case_destringify_string;
+
+ /** \test
+ * Test destringify for bool.
+ *
+ * \ingroup grptestcases
+ */
+ struct DestringifyBoolTests : TestCase
+ {
+ DestringifyBoolTests() : TestCase("destringify bool") { }
+
+ void run()
+ {
+ TEST_CHECK( destringify<bool>("true"));
+ TEST_CHECK( destringify<bool>("1"));
+ TEST_CHECK( destringify<bool>("5"));
+ TEST_CHECK(!destringify<bool>("false"));
+ TEST_CHECK(!destringify<bool>("0"));
+ TEST_CHECK(!destringify<bool>("-1"));
+ TEST_CHECK_THROWS(destringify<bool>("flase"), DestringifyError);
+ TEST_CHECK_THROWS(destringify<bool>("432.2413"), DestringifyError);
+ }
+ } test_case_destringify_bool;
+}
+
diff --git a/0.4.0/paludis/util/dir_iterator.cc b/0.4.0/paludis/util/dir_iterator.cc
new file mode 100644
index 000000000..4e9493117
--- /dev/null
+++ b/0.4.0/paludis/util/dir_iterator.cc
@@ -0,0 +1,143 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/stringify.hh>
+#include <sys/types.h>
+
+/** \file
+ * Implementation of paludis::DirIterator.
+ *
+ * \ingroup grpfilesystem
+ */
+
+using namespace paludis;
+
+DirOpenError::DirOpenError(const FSEntry & location, const int errno_value) throw () :
+ FSError("Error opening directory '" + stringify(location) + "': " + strerror(errno_value))
+{
+}
+
+DirIterator::DirIterator(const FSEntry & base, bool ignore_dotfiles) :
+ _base(base),
+ _ignore_dotfiles(ignore_dotfiles),
+ _items(new std::set<FSEntry>)
+{
+ DIR * d(opendir(stringify(base).c_str()));
+ if (0 == d)
+ throw DirOpenError(base, errno);
+
+ struct dirent * de;
+ while (0 != ((de = readdir(d))))
+ if (ignore_dotfiles)
+ {
+ if ('.' != de->d_name[0])
+ _items->insert(_base / std::string(de->d_name));
+ }
+ else if (! (de->d_name[0] == '.' &&
+ (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))))
+ _items->insert(_base / std::string(de->d_name));
+
+ _iter = _items->begin();
+
+ closedir(d);
+}
+
+DirIterator::DirIterator(const DirIterator & other) :
+ _base(other._base),
+ _ignore_dotfiles(other._ignore_dotfiles),
+ _items(other._items),
+ _iter(other._iter)
+{
+}
+
+DirIterator::DirIterator() :
+ _base(""),
+ _items(new std::set<FSEntry>),
+ _iter(_items->end())
+{
+}
+
+DirIterator::~DirIterator()
+{
+}
+
+const DirIterator &
+DirIterator::operator= (const DirIterator & other)
+{
+ if (this != &other)
+ {
+ _base = other._base;
+ _items = other._items;
+ _iter = other._iter;
+ }
+ return *this;
+}
+
+const FSEntry &
+DirIterator::operator* () const
+{
+ return *_iter;
+}
+
+const FSEntry *
+DirIterator::operator-> () const
+{
+ return &*_iter;
+}
+
+DirIterator &
+DirIterator::operator++ ()
+{
+ ++_iter;
+ return *this;
+}
+
+DirIterator
+DirIterator::operator++ (int)
+{
+ DirIterator c(*this);
+ _iter++;
+ return c;
+}
+
+bool
+DirIterator::operator== (const DirIterator & other) const
+{
+ if (other._iter == other._items->end())
+ return _iter == _items->end();
+
+ if (_iter == _items->end())
+ return other._iter == other._items->end();
+
+ if (other._items != _items)
+ throw InternalError(PALUDIS_HERE,
+ "comparing two different DirIterators.");
+
+ return other._iter == _iter;
+}
+
+bool
+DirIterator::operator!= (const DirIterator & other) const
+{
+ return ! operator== (other);
+}
+
diff --git a/0.4.0/paludis/util/dir_iterator.hh b/0.4.0/paludis/util/dir_iterator.hh
new file mode 100644
index 000000000..43062eadf
--- /dev/null
+++ b/0.4.0/paludis/util/dir_iterator.hh
@@ -0,0 +1,127 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_DIR_ITERATOR_HH
+#define PALUDIS_GUARD_PALUDIS_DIR_ITERATOR_HH 1
+
+#include <iterator>
+#include <paludis/util/counted_ptr.hh>
+#include <paludis/util/fs_entry.hh>
+#include <set>
+
+/** \file
+ * Declarations for paludis::DirIterator.
+ *
+ * \ingroup grpfilesystem
+ */
+
+namespace paludis
+{
+ /**
+ * Raised when a directory open fails.
+ *
+ * \ingroup grpfilesystem
+ * \ingroup grpexceptions
+ */
+ class DirOpenError : public FSError
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ DirOpenError(const FSEntry & location, const int errno_value) throw ();
+ };
+
+ /**
+ * An iterator that iterates over the contents of a directory. At present,
+ * we read in all the entries at creation time and maintain a CountedPtr
+ * to an ordered set of FSEntry instances. This may change at some point,
+ * if it turns out that it's quicker to use opendir and seekdir for each
+ * instance.
+ *
+ * \ingroup grpfilesystem
+ */
+ class DirIterator : public std::iterator<std::forward_iterator_tag, FSEntry>
+ {
+ private:
+ FSEntry _base;
+ bool _ignore_dotfiles;
+ CountedPtr<std::set<FSEntry>, count_policy::ExternalCountTag> _items;
+ std::set<FSEntry>::iterator _iter;
+
+ public:
+ /**
+ * Constructor, to an FSEntry which must be a directory, with an
+ * option to not ignore dotfiles.
+ */
+ explicit DirIterator(const FSEntry & base, bool ignore_dotfiles = true);
+
+ /**
+ * Copy constructor.
+ */
+ DirIterator(const DirIterator & other);
+
+ /**
+ * Constructor, creates an end() iterator.
+ */
+ DirIterator();
+
+ /**
+ * Destructor.
+ */
+ ~DirIterator();
+
+ /**
+ * Assign.
+ */
+ const DirIterator & operator= (const DirIterator & other);
+
+ /**
+ * Dereference.
+ */
+ const FSEntry & operator* () const;
+
+ /**
+ * Dereference.
+ */
+ const FSEntry * operator-> () const;
+
+ /**
+ * Increment.
+ */
+ DirIterator & operator++ ();
+
+ /**
+ * Increment.
+ */
+ DirIterator operator++ (int);
+
+ /**
+ * Compare.
+ */
+ bool operator== (const DirIterator & other) const;
+
+ /**
+ * Inverse compare.
+ */
+ bool operator!= (const DirIterator & other) const;
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/dir_iterator_TEST.cc b/0.4.0/paludis/util/dir_iterator_TEST.cc
new file mode 100644
index 000000000..217f48a4e
--- /dev/null
+++ b/0.4.0/paludis/util/dir_iterator_TEST.cc
@@ -0,0 +1,97 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Mark Loeser <halcy0n@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/dir_iterator.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Test cases for dir_iterator.hh.
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test DirIterator construction and manipulation.
+ *
+ * \ingroup grptestcases
+ */
+ struct DirIteratorManipulationTest : TestCase
+ {
+ DirIteratorManipulationTest() : TestCase("construction and manipulation") { }
+
+ void run()
+ {
+ TEST_CHECK_THROWS(DirIterator(FSEntry("/i/dont/exist/")), DirOpenError);
+
+ DirIterator iter(FSEntry("dir_iterator_TEST_dir"));
+ DirIterator iter1(iter);
+ TEST_CHECK(iter == iter1);
+ TEST_CHECK(!(iter != iter1));
+ }
+ } test_dir_iterator_manipulation;
+
+ /**
+ * \test Test DirIterator iterating abilities
+ *
+ * \ingroup grptestcases
+ */
+ struct DirIteratorIterateTest : TestCase
+ {
+ DirIteratorIterateTest() : TestCase("iterate") {}
+
+ void run()
+ {
+ DirIterator iter(FSEntry("dir_iterator_TEST_dir"));
+ DirIterator iter1(FSEntry("dir_iterator_TEST_dir"));
+ DirIterator iter2(FSEntry("dir_iterator_TEST_dir"), false);
+
+ TEST_CHECK(iter != DirIterator());
+ TEST_CHECK(DirIterator() != iter);
+
+ TEST_CHECK_EQUAL(iter->basename(), "file1");
+ TEST_CHECK(++iter != DirIterator());
+ TEST_CHECK_EQUAL(iter->basename(), "file2");
+ TEST_CHECK(++iter == DirIterator());
+ TEST_CHECK(DirIterator() == iter);
+
+ while (iter1 != DirIterator())
+ ++iter1;
+ TEST_CHECK(iter1 == DirIterator());
+ TEST_CHECK(iter == iter1);
+
+ TEST_CHECK_EQUAL(iter2->basename(), ".file3");
+ TEST_CHECK(++iter2 != DirIterator());
+ TEST_CHECK_EQUAL(iter2->basename(), "file1");
+ TEST_CHECK(++iter2 != DirIterator());
+ TEST_CHECK_EQUAL(iter2->basename(), "file2");
+ TEST_CHECK(++iter2 == DirIterator());
+ TEST_CHECK(DirIterator() == iter2);
+ TEST_CHECK(iter2 == DirIterator());
+
+ TEST_CHECK(iter1 == iter2);
+ TEST_CHECK(iter2 == iter1);
+ }
+ } test_dir_iterator_iterate;
+}
diff --git a/0.4.0/paludis/util/dir_iterator_TEST_cleanup.sh b/0.4.0/paludis/util/dir_iterator_TEST_cleanup.sh
new file mode 100755
index 000000000..17378f3c1
--- /dev/null
+++ b/0.4.0/paludis/util/dir_iterator_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d dir_iterator_TEST_dir ] ; then
+ rm -fr dir_iterator_TEST_dir
+else
+ true
+fi
+
diff --git a/0.4.0/paludis/util/dir_iterator_TEST_setup.sh b/0.4.0/paludis/util/dir_iterator_TEST_setup.sh
new file mode 100755
index 000000000..930f2f5a7
--- /dev/null
+++ b/0.4.0/paludis/util/dir_iterator_TEST_setup.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir dir_iterator_TEST_dir || exit 2
+cd dir_iterator_TEST_dir || exit 3
+touch file1 file2 .file3 || exit 4
diff --git a/0.4.0/paludis/util/exception.cc b/0.4.0/paludis/util/exception.cc
new file mode 100644
index 000000000..07d787e46
--- /dev/null
+++ b/0.4.0/paludis/util/exception.cc
@@ -0,0 +1,64 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/exception.hh>
+
+/** \file
+ * Exception class implementations.
+ *
+ * \ingroup grpexceptions
+ */
+
+using namespace paludis;
+
+Exception::Exception(const std::string & message) throw () :
+ _message(message)
+{
+}
+
+Exception::~Exception() throw ()
+{
+}
+
+const std::string &
+Exception::message() const throw ()
+{
+ return _message;
+}
+
+InternalError::InternalError(const std::string & where, const std::string & message) throw () :
+ Exception("Eek! Internal error at " + where + ": " + message)
+{
+}
+
+InternalError::InternalError(const std::string & where) throw () :
+ Exception("Eek! Internal error at " + where)
+{
+}
+
+NameError::NameError(const std::string & name, const std::string & role) throw () :
+ Exception("Name '" + name + "' is not a valid " + role)
+{
+}
+
+ConfigurationError::ConfigurationError(const std::string & msg) throw () :
+ Exception(msg)
+{
+}
+
diff --git a/0.4.0/paludis/util/exception.hh b/0.4.0/paludis/util/exception.hh
new file mode 100644
index 000000000..6f93ba8e3
--- /dev/null
+++ b/0.4.0/paludis/util/exception.hh
@@ -0,0 +1,158 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_EXCEPTION_HH
+#define PALUDIS_GUARD_PALUDIS_EXCEPTION_HH 1
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/stringify.hh>
+
+#include <string>
+#include <exception>
+#include <libebt/libebt.hh>
+
+/** \file
+ * Declaration for the Exception base class, the InternalError exception
+ * class, the NameError class and related utilities.
+ *
+ * \ingroup grpexceptions
+ */
+
+namespace paludis
+{
+ /**
+ * Context tag for libebt.
+ *
+ * \ingroup grpexceptions
+ */
+ struct PaludisBacktraceTag
+ {
+ };
+
+ /**
+ * Backtrace context class.
+ *
+ * \ingroup grpexceptions
+ */
+ typedef libebt::BacktraceContext<PaludisBacktraceTag> Context;
+
+ /**
+ * Base exception class.
+ *
+ * \ingroup grpexceptions
+ */
+ class Exception :
+ public std::exception,
+ public libebt::Backtraceable<PaludisBacktraceTag>
+ {
+ private:
+ const std::string _message;
+
+ protected:
+ /**
+ * Constructor.
+ */
+ Exception(const std::string & message) throw ();
+
+ public:
+ /**
+ * Destructor.
+ */
+ virtual ~Exception() throw () PALUDIS_ATTRIBUTE((nothrow));
+
+ /**
+ * Return our descriptive error message.
+ */
+ const std::string & message() const throw () PALUDIS_ATTRIBUTE((nothrow));
+ };
+
+ /**
+ * An InternalError is an Exception that is thrown if something that is
+ * never supposed to happen happens.
+ *
+ * \ingroup grpexceptions
+ */
+ class InternalError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ *
+ * \param where Should be set to the PALUDIS_HERE macro.
+ *
+ * \param message A short message.
+ */
+ InternalError(const std::string & where, const std::string & message) throw ();
+
+ /**
+ * Constructor, with no message (deprecated).
+ *
+ * \param where Should be set to the PALUDIS_HERE macro.
+ *
+ * \deprecated Use paludis::InternalError::InternalError(const char * const,
+ * const std::string &) instead.
+ */
+ InternalError(const std::string & where) throw () PALUDIS_ATTRIBUTE((deprecated));
+ };
+
+ /**
+ * A NameError is an Exception that is thrown when some kind of invalid
+ * name is encountered.
+ *
+ * \ingroup grpexceptions
+ * \ingroup grpnames
+ */
+ class NameError : public Exception
+ {
+ protected:
+ /**
+ * Constructor.
+ *
+ * \param name The invalid name encountered.
+ * \param role The role for the name, for example "package name".
+ */
+ NameError(const std::string & name, const std::string & role) throw ();
+ };
+
+ /**
+ * A ConfigurationError is thrown when an invalid configuration occurs.
+ *
+ * \ingroup grpexceptions
+ * \ingroup grpconfigfile
+ */
+ class ConfigurationError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ ConfigurationError(const std::string & msg) throw ();
+ };
+}
+
+/** \def PALUDIS_HERE
+ * Expands to the current function name, file and line, for use with
+ * paludis::InternalError.
+ *
+ * \ingroup grpexceptions
+ */
+#define PALUDIS_HERE (paludis::stringify(__PRETTY_FUNCTION__) + " at " + \
+ paludis::stringify(__FILE__) + ":" + paludis::stringify(__LINE__))
+
+#endif
diff --git a/0.4.0/paludis/util/files.m4 b/0.4.0/paludis/util/files.m4
new file mode 100644
index 000000000..612734dc6
--- /dev/null
+++ b/0.4.0/paludis/util/files.m4
@@ -0,0 +1,41 @@
+dnl vim: set ft=m4 et :
+dnl This file is used by Makefile.am.m4. You should use the provided
+dnl autogen.bash script to do all the hard work.
+dnl
+dnl This file is used to avoid having to make lots of repetitive changes in
+dnl Makefile.am every time we add a source or test file. The first parameter is
+dnl the base filename with no extension; later parameters can be `hh', `cc',
+dnl `test', `impl', `testscript'. Note that there isn't much error checking done
+dnl on this file at present...
+
+add(`attributes', `hh')
+add(`collection', `hh')
+add(`compare', `hh')
+add(`comparison_policy', `hh', `test')
+add(`composite_pattern', `hh')
+add(`container_entry', `hh', `test')
+add(`counted_ptr', `hh', `cc', `test')
+add(`deleter', `hh', `cc', `test')
+add(`destringify', `hh', `cc', `test')
+add(`dir_iterator', `hh', `cc', `test', `testscript')
+add(`exception', `hh', `cc')
+add(`fs_entry', `hh', `cc', `test', `testscript')
+add(`iterator', `hh', `test')
+add(`instantiation_policy', `hh', `test')
+add(`is_file_with_extension', `hh', `cc', `test', `testscript')
+add(`join', `hh', `test')
+add(`log', `hh', `cc', `test')
+add(`private_implementation_pattern', `hh')
+add(`pstream', `hh', `cc', `test')
+add(`random', `hh', `cc', `test')
+add(`save', `hh', `test')
+add(`smart_record', `hh', `test')
+add(`stringify', `hh', `test')
+add(`strip', `hh', `cc', `test')
+add(`system', `hh', `cc', `test', `testscript')
+add(`tokeniser', `hh', `cc', `test')
+add(`util', `hh')
+add(`validated', `hh', `test')
+add(`virtual_constructor', `hh', `test')
+add(`visitor', `hh', `test')
+
diff --git a/0.4.0/paludis/util/fs_entry.cc b/0.4.0/paludis/util/fs_entry.cc
new file mode 100644
index 000000000..7a9545770
--- /dev/null
+++ b/0.4.0/paludis/util/fs_entry.cc
@@ -0,0 +1,439 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ * Copyright (c) 2006 Mark Loeser <halcy0n@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/exception.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/stringify.hh>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <cstring>
+#include <cstdlib>
+
+/** \file
+ * Implementation of paludis::FSEntry.
+ *
+ * \ingroup grpfilesystem
+ */
+
+using namespace paludis;
+
+FSError::FSError(const std::string & message) throw () :
+ Exception(message)
+{
+}
+
+FSEntry::FSEntry(const std::string & path) :
+ ComparisonPolicyType(&FSEntry::_path),
+ _path(path),
+ _stat_info(0),
+ _exists(false),
+ _checked(false)
+{
+ _normalise();
+}
+
+FSEntry::FSEntry(const FSEntry & other) :
+ ComparisonPolicyType(&FSEntry::_path),
+ _path(other._path),
+ _stat_info(other._stat_info),
+ _exists(other._exists),
+ _checked(other._checked)
+{
+}
+
+FSEntry::~FSEntry()
+{
+}
+
+const FSEntry &
+FSEntry::operator= (const FSEntry & other)
+{
+ _path = other._path;
+ _stat_info = other._stat_info;
+ _exists = other._exists;
+ _checked = other._checked;
+
+ return *this;
+}
+
+FSEntry
+FSEntry::operator/ (const FSEntry & rhs) const
+{
+ return FSEntry(_path + "/" + rhs._path);
+}
+
+FSEntry
+FSEntry::operator/ (const std::string & rhs) const
+{
+ return operator/ (FSEntry(rhs));
+}
+
+const FSEntry &
+FSEntry::operator/= (const FSEntry & rhs)
+{
+ _path.append("/");
+ _path.append(rhs._path);
+ _normalise();
+
+ _checked = false;
+ _exists = false;
+ _stat_info = CountedPtr<struct ::stat, count_policy::ExternalCountTag>(0);
+
+ return *this;
+}
+
+bool
+FSEntry::exists() const
+{
+ _stat();
+
+ return _exists;
+}
+
+bool
+FSEntry::is_directory() const
+{
+ _stat();
+
+ if (_exists)
+ return S_ISDIR((*_stat_info).st_mode);
+
+ return false;
+}
+
+bool
+FSEntry::is_regular_file() const
+{
+ _stat();
+
+ if (_exists)
+ return S_ISREG((*_stat_info).st_mode);
+
+ return false;
+}
+
+bool
+FSEntry::is_symbolic_link() const
+{
+ _stat();
+
+ if (_exists)
+ return S_ISLNK((*_stat_info).st_mode);
+
+ return false;
+}
+
+
+bool
+FSEntry::has_permission(const FSUserGroup & user_group, const FSPermission & fs_perm) const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ switch (user_group)
+ {
+ case fs_ug_owner:
+ {
+ switch (fs_perm)
+ {
+ case fs_perm_read:
+ return (*_stat_info).st_mode & S_IRUSR;
+ case fs_perm_write:
+ return (*_stat_info).st_mode & S_IWUSR;
+ case fs_perm_execute:
+ return (*_stat_info).st_mode & S_IXUSR;
+ }
+ throw InternalError(PALUDIS_HERE, "Unhandled FSPermission");
+ }
+ case fs_ug_group:
+ {
+ switch (fs_perm)
+ {
+ case fs_perm_read:
+ return (*_stat_info).st_mode & S_IRGRP;
+ case fs_perm_write:
+ return (*_stat_info).st_mode & S_IWGRP;
+ case fs_perm_execute:
+ return (*_stat_info).st_mode & S_IXGRP;
+ }
+ throw InternalError(PALUDIS_HERE, "Unhandled FSPermission");
+ }
+ case fs_ug_others:
+ {
+ switch (fs_perm)
+ {
+ case fs_perm_read:
+ return (*_stat_info).st_mode & S_IROTH;
+ case fs_perm_write:
+ return (*_stat_info).st_mode & S_IWOTH;
+ case fs_perm_execute:
+ return (*_stat_info).st_mode & S_IXOTH;
+ }
+ throw InternalError(PALUDIS_HERE, "Unhandled FSPermission");
+ }
+ }
+
+ throw InternalError(PALUDIS_HERE, "Unhandled FSUserGroup");
+}
+
+mode_t
+FSEntry::permissions() const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ return _stat_info->st_mode;
+}
+
+void
+FSEntry::_normalise()
+{
+ try
+ {
+ std::string new_path;
+ std::string::size_type p(0);
+ while (p < _path.length())
+ {
+ if ('/' == _path[p])
+ {
+ new_path += '/';
+ while (++p < _path.length())
+ if ('/' != _path[p])
+ break;
+ }
+ else
+ new_path += _path[p++];
+ }
+ _path = new_path;
+
+ if (! _path.empty())
+ if ('/' == _path.at(_path.length() - 1))
+ _path.erase(_path.length() - 1);
+ if (_path.empty())
+ _path = "/";
+ }
+ catch (const std::exception & e)
+ {
+ Context c("When normalising FSEntry path '" + _path + "':");
+ throw InternalError(PALUDIS_HERE,
+ "caught std::exception '" + stringify(e.what()) + "'");
+ }
+}
+
+void
+FSEntry::_stat() const
+{
+ if (_checked)
+ return;
+
+ _stat_info = CountedPtr<struct stat, count_policy::ExternalCountTag>(new struct stat);
+ if (0 != lstat(_path.c_str(), _stat_info.raw_pointer()))
+ {
+ if (errno != ENOENT)
+ throw FSError("Error running stat() on '" + stringify(_path) + "': "
+ + strerror(errno));
+
+ _exists = false;
+ _stat_info = CountedPtr<struct stat, count_policy::ExternalCountTag>(0);
+ }
+ else
+ _exists = true;
+
+ _checked = true;
+}
+
+std::string
+FSEntry::basename() const
+{
+ if (_path == "/")
+ return _path;
+
+ return _path.substr(_path.rfind('/') + 1);
+}
+
+FSEntry
+FSEntry::dirname() const
+{
+ if (_path == "/")
+ return FSEntry(_path);
+
+ return FSEntry(_path.substr(0, _path.rfind('/')));
+}
+
+FSEntry
+FSEntry::realpath() const
+{
+ char r[PATH_MAX + 1];
+ std::memset(r, 0, PATH_MAX + 1);
+ if (! ::realpath(_path.c_str(), r))
+ throw FSError("Could not resolve path '" + _path + "'");
+ return FSEntry(r);
+}
+
+FSEntry
+FSEntry::cwd()
+{
+ char r[PATH_MAX + 1];
+ std::memset(r, 0, PATH_MAX + 1);
+ if (! ::getcwd(r, PATH_MAX))
+ throw FSError("Could not get current working directory");
+ return FSEntry(r);
+}
+
+std::ostream &
+paludis::operator<< (std::ostream & s, const FSEntry & f)
+{
+ s << f._path;
+ return s;
+}
+
+time_t
+FSEntry::ctime() const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ return (*_stat_info).st_ctime;
+}
+
+time_t
+FSEntry::mtime() const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ return (*_stat_info).st_mtime;
+}
+
+off_t
+FSEntry::file_size() const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ if (! is_regular_file())
+ throw FSError("file_size called on non-regular file '" + _path + "'");
+
+ return _stat_info->st_size;
+}
+
+bool
+FSEntry::mkdir(mode_t mode)
+{
+ if (0 == ::mkdir(_path.c_str(), mode))
+ return true;
+
+ int e(errno);
+ if (e == EEXIST)
+ {
+ if (is_directory())
+ return false;
+ throw FSError("mkdir '" + _path + "' failed: target exists and is not a directory");
+ }
+ else
+ throw FSError("mkdir '" + _path + "' failed: " + ::strerror(e));
+}
+
+bool
+FSEntry::unlink()
+{
+ if (0 == ::unlink(_path.c_str()))
+ return true;
+
+ int e(errno);
+ if (e == ENOENT)
+ return false;
+ else
+ throw FSError("unlink '" + _path + "' failed: " + ::strerror(e));
+}
+
+bool
+FSEntry::rmdir()
+{
+ if (0 == ::rmdir(_path.c_str()))
+ return true;
+
+ int e(errno);
+ if (e == ENOENT)
+ return false;
+ else
+ throw FSError("rmdir '" + _path + "' failed: " + ::strerror(e));
+}
+
+std::string
+FSEntry::readlink() const
+{
+ char buf[PATH_MAX + 1];
+ std::memset(buf, 0, PATH_MAX + 1);
+ if (-1 == ::readlink(_path.c_str(), buf, PATH_MAX))
+ throw FSError("readlink '" + _path + "' failed: " + ::strerror(errno));
+ return buf;
+}
+
+void
+FSEntry::chown(const uid_t owner, const gid_t group)
+{
+ if (0 != ::chown(_path.c_str(), owner, group))
+ throw FSError("chown '" + _path + "' failed: " + ::strerror(errno));
+}
+
+void
+FSEntry::chmod(const mode_t mode)
+{
+ if (0 != ::chmod(_path.c_str(), mode))
+ throw FSError("chmod '" + _path + "' failed: " + ::strerror(errno));
+}
+
+uid_t
+FSEntry::owner() const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ return _stat_info->st_uid;
+}
+
+gid_t
+FSEntry::group() const
+{
+ _stat();
+
+ if (! _exists)
+ throw FSError("Filesystem entry '" + _path + "' does not exist");
+
+ return _stat_info->st_gid;
+}
+
diff --git a/0.4.0/paludis/util/fs_entry.hh b/0.4.0/paludis/util/fs_entry.hh
new file mode 100644
index 000000000..bf666eee7
--- /dev/null
+++ b/0.4.0/paludis/util/fs_entry.hh
@@ -0,0 +1,319 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ * Copyright (c) 2006 Mark Loeser <halcy0n@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_FS_ENTRY_HH
+#define PALUDIS_GUARD_PALUDIS_FS_ENTRY_HH 1
+
+#include <paludis/util/comparison_policy.hh>
+#include <paludis/util/counted_ptr.hh>
+#include <paludis/util/exception.hh>
+#include <string>
+#include <iosfwd>
+
+/** \file
+ * Declarations for paludis::Filesystem.
+ *
+ * \ingroup grpfilesystem
+ */
+
+struct stat;
+
+namespace paludis
+{
+ /**
+ * Generic filesystem error class.
+ *
+ * \ingroup grpexceptions
+ * \ingroup grpfilesystem
+ */
+ class FSError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ FSError(const std::string & message) throw ();
+ };
+
+ /**
+ * File permissions used by FSEntry.
+ *
+ * \ingroup grpfilesystem
+ */
+ enum FSPermission
+ {
+ fs_perm_read, ///< read permission on file
+ fs_perm_write, ///< write permission on file
+ fs_perm_execute ///< execute permission on file
+ };
+
+ /**
+ * User classes used by FSEntry.
+ *
+ * \ingroup grpfilesystem
+ */
+ enum FSUserGroup
+ {
+ fs_ug_owner, ///< owner permission
+ fs_ug_group, ///< group permission
+ fs_ug_others ///< others permission
+ };
+
+ /**
+ * Represents an entry (which may or may not exist) in the filesystem.
+ *
+ * \ingroup grpfilesystem
+ */
+ class FSEntry : public ComparisonPolicy<
+ FSEntry,
+ comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<std::string> >
+ {
+ friend std::ostream & operator<< (std::ostream & s, const FSEntry & f);
+
+ private:
+ std::string _path;
+
+ mutable CountedPtr<struct ::stat, count_policy::ExternalCountTag> _stat_info;
+
+ mutable bool _exists;
+
+ /**
+ * Whether or not we have run _stat() on this location yet
+ */
+ mutable bool _checked;
+
+ void _normalise();
+
+ /**
+ * Runs lstat() on the current path if we have not done so already
+ * Note: lstat() will give information on the symlink itself, and not what
+ * the link points to, which is how stat() works.
+ */
+ void _stat() const;
+
+ public:
+ /**
+ * Constructor, from a path.
+ */
+ FSEntry(const std::string & path);
+
+ /**
+ * Copy constructor.
+ */
+ FSEntry(const FSEntry & other);
+
+ /**
+ * Destructor.
+ */
+ ~FSEntry();
+
+ /**
+ * Assignment, from another FSEntry.
+ */
+ const FSEntry & operator= (const FSEntry & other);
+
+ /**
+ * Join with another FSEntry.
+ */
+ FSEntry operator/ (const FSEntry & rhs) const;
+
+ /**
+ * Append another FSEntry.
+ */
+ const FSEntry & operator/= (const FSEntry & rhs);
+
+ /**
+ * Join with another path.
+ */
+ FSEntry operator/ (const std::string & rhs) const;
+
+ /**
+ * Append another path.
+ */
+ const FSEntry & operator/= (const std::string & rhs)
+ {
+ return operator/= (FSEntry(rhs));
+ }
+
+ /**
+ * Does a filesystem entry exist at our location?
+ */
+ bool exists() const;
+
+ /**
+ * Does a filesystem entry exist at our location, and if it does,
+ * is it a directory?
+ */
+ bool is_directory() const;
+
+ /**
+ * Does a filesystem entry exist at our location, and if it does,
+ * is it a regular file?
+ */
+ bool is_regular_file() const;
+
+ /**
+ * Does a filesystem entry exist at our location, and if it does,
+ * is it a symbolic link?
+ */
+ bool is_symbolic_link() const;
+
+ /**
+ * Check if filesystem entry has `perm` for `user_group`.
+ *
+ * \exception FSError if there was a problem accessing the filesystem entry
+ */
+ bool has_permission(const FSUserGroup & user_group, const FSPermission & fs_perm) const;
+
+ /**
+ * Return the permissions for our item.
+ *
+ * \exception FSError if there was a problem accessing the filesystem entry
+ */
+ mode_t permissions() const;
+
+ /**
+ * Return the last part of our path (eg '/foo/bar' => 'bar').
+ */
+ std::string basename() const;
+
+ /**
+ * Return the first part of our path (eg '/foo/bar' => '/foo').
+ */
+ FSEntry dirname() const;
+
+ /**
+ * Return the canonicalised version of our path.
+ */
+ FSEntry realpath() const;
+
+ /**
+ * Return our destination, if we are a symlink.
+ *
+ * \exception FSError if we are not a symlink, or if the system call
+ * fails.
+ */
+ std::string readlink() const;
+
+ /**
+ * Return the time the filesystem entry was created
+ * \exception FSError if there was a problem accessing the filesystem entry
+ */
+ time_t ctime() const;
+
+ /**
+ * Return the time the filesystem entry was last modified
+ * \exception FSError if there was a problem accessing the filesystem entry
+ */
+ time_t mtime() const;
+
+ /**
+ * Return the size of our file, in bytes.
+ *
+ * \exception FSError if we don't have a size.
+ */
+ off_t file_size() const;
+
+ /**
+ * Try to make a directory.
+ *
+ * \return True, if we succeeded, and false if the directory
+ * already exists and is a directory.
+ *
+ * \exception FSError If an error other than the directory already
+ * existing occurs.
+ */
+ bool mkdir(const mode_t mode = 0755);
+
+ /**
+ * Try to unlink.
+ *
+ * \return True, if we succeeded, and false if we don't exist
+ * already.
+ *
+ * \exception FSError If an error other than us already not
+ * existing occurs.
+ */
+ bool unlink();
+
+ /**
+ * Try to rmdir.
+ *
+ * \return True, if we succeeded, and false if we don't exist
+ * already.
+ *
+ * \exception FSError If an error other than us already not
+ * existing occurs.
+ */
+ bool rmdir();
+
+ /**
+ * Change our permissions.
+ *
+ * \exception FSError If the chown failed.
+ */
+ void chown(const uid_t owner, const gid_t group);
+
+ /**
+ * Change our permissions.
+ *
+ * \exception FSError If the chmod failed.
+ */
+ void chmod(const mode_t mode);
+
+ /**
+ * Fetch our owner.
+ *
+ * \exception FSError If we don't exist or the stat call fails.
+ */
+ uid_t owner() const;
+
+ /**
+ * Fetch our group.
+ *
+ * \exception FSError If we don't exist or the stat call fails.
+ */
+ gid_t group() const;
+
+ /**
+ * Return the current working directory
+ */
+ static FSEntry cwd();
+ };
+
+ /**
+ * An FSEntry can be written to an ostream.
+ *
+ * \ingroup grpfilesystem
+ */
+ std::ostream & operator<< (std::ostream & s, const FSEntry & f);
+
+ template <typename T_> class SequentialCollection;
+
+ /**
+ * An ordered group of FSEntry instances.
+ *
+ * \ingroup grpfilesystem
+ */
+ typedef SequentialCollection<FSEntry> FSEntryCollection;
+}
+
+#endif
diff --git a/0.4.0/paludis/util/fs_entry_TEST.cc b/0.4.0/paludis/util/fs_entry_TEST.cc
new file mode 100644
index 000000000..f3dc2bfb1
--- /dev/null
+++ b/0.4.0/paludis/util/fs_entry_TEST.cc
@@ -0,0 +1,242 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ * Copyright (c) 2006 Mark Loeser <halcy0n@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ctime>
+#include <paludis/util/fs_entry.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Test cases for fs_entry.hh.
+ *
+ * \todo this is nowhere near complete.
+ *
+ * \ingroup grpfilesystem
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test FSEntry construction and manipulation.
+ *
+ * \ingroup grpfilesystem
+ */
+ struct FSEntryManipulationTest : TestCase
+ {
+ FSEntryManipulationTest() : TestCase("construction and manipulation") { }
+
+ void run()
+ {
+ FSEntry f("/foo/bar");
+ FSEntry c(f);
+ TEST_CHECK_EQUAL(f, FSEntry("/foo/bar"));
+ TEST_CHECK_EQUAL(c, FSEntry("/foo/bar"));
+ f = FSEntry("/baz");
+ TEST_CHECK_EQUAL(f, FSEntry("/baz"));
+ TEST_CHECK_EQUAL(c, FSEntry("/foo/bar"));
+ c /= "moo";
+ TEST_CHECK_EQUAL(f, FSEntry("/baz"));
+ TEST_CHECK_EQUAL(c, FSEntry("/foo/bar/moo"));
+ f = c / f;
+ TEST_CHECK_EQUAL(f, FSEntry("/foo/bar/moo/baz"));
+ TEST_CHECK_EQUAL(c, FSEntry("/foo/bar/moo"));
+
+ f = FSEntry::cwd();
+ }
+ } test_fs_entry_manipulation;
+
+ /**
+ * \test Test FSEntry behavior.
+ *
+ * \ingroup grpfilesystem
+ */
+ struct FSEntryRealpathTest : TestCase
+ {
+ FSEntryRealpathTest() : TestCase("behavior") { }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+
+ void run()
+ {
+ FSEntry d("fs_entry_TEST_dir");
+ TEST_CHECK(d.is_directory());
+
+ d /= "all_perms";
+ TEST_CHECK(d.is_regular_file());
+
+ FSEntry e("fs_entry_TEST_dir/nosuchfile");
+ TEST_CHECK(! e.is_regular_file());
+ d = e;
+ TEST_CHECK(! d.is_regular_file());
+ TEST_CHECK(! d.exists());
+
+ d = FSEntry("fs_entry_TEST_dir/all_perms");
+ TEST_CHECK(! e.is_regular_file());
+ TEST_CHECK(d.is_regular_file());
+ TEST_CHECK(d.exists());
+
+ d = FSEntry("fs_entry_TEST_dir/symlink_to_dir_a");
+ TEST_CHECK(d.is_symbolic_link());
+ TEST_CHECK(! d.is_directory());
+ TEST_CHECK(! d.is_regular_file());
+
+ FSEntry f("fs_entry_TEST_dir/symlink_to_dir_a/file_in_a");
+ TEST_CHECK(f.is_regular_file());
+ TEST_CHECK(! f.is_symbolic_link());
+ FSEntry r(f.realpath());
+ TEST_CHECK(r.is_regular_file());
+ std::string g("fs_entry_TEST_dir/dir_a/file_in_a");
+ TEST_CHECK_EQUAL(stringify(r).substr(stringify(r).length() - g.length()), g);
+
+ FSEntry h("fs_entry_TEST_dir/symlink_to_file_in_a");
+ TEST_CHECK(h.is_symbolic_link());
+ TEST_CHECK(! h.is_regular_file());
+
+ FSEntry i("fs_entry_TEST_dir/dir_to_make");
+ TEST_CHECK(i.mkdir());
+ TEST_CHECK(! i.mkdir());
+
+ FSEntry j("fs_entry_TEST_dir/dir_a/file_in_a");
+ TEST_CHECK_THROWS(j.mkdir(), FSError);
+ }
+ } test_fs_entry_behaviour;
+
+ /**
+ * \test Test FSEntry has_permission methods.
+ *
+ * \ingroup grpfilesystem
+ */
+ struct FSEntryHasPermission: TestCase
+ {
+ FSEntryHasPermission() : TestCase("has_permission") {}
+
+ void run()
+ {
+ FSEntry a("fs_entry_TEST_dir/all_perms");
+ FSEntry b("fs_entry_TEST_dir/no_perms");
+ FSEntry c("fs_entry_TEST_dir/no_such_file");
+
+ TEST_CHECK(a.has_permission(fs_ug_owner, fs_perm_read));
+ TEST_CHECK(a.has_permission(fs_ug_owner, fs_perm_write));
+ TEST_CHECK(a.has_permission(fs_ug_owner, fs_perm_execute));
+ TEST_CHECK(a.has_permission(fs_ug_group, fs_perm_read));
+ TEST_CHECK(a.has_permission(fs_ug_group, fs_perm_write));
+ TEST_CHECK(a.has_permission(fs_ug_group, fs_perm_execute));
+ TEST_CHECK(a.has_permission(fs_ug_others, fs_perm_read));
+ TEST_CHECK(a.has_permission(fs_ug_others, fs_perm_write));
+ TEST_CHECK(a.has_permission(fs_ug_others, fs_perm_execute));
+
+ TEST_CHECK(!b.has_permission(fs_ug_owner, fs_perm_read));
+ TEST_CHECK(!b.has_permission(fs_ug_owner, fs_perm_write));
+ TEST_CHECK(!b.has_permission(fs_ug_owner, fs_perm_execute));
+ TEST_CHECK(!b.has_permission(fs_ug_group, fs_perm_read));
+ TEST_CHECK(!b.has_permission(fs_ug_group, fs_perm_write));
+ TEST_CHECK(!b.has_permission(fs_ug_group, fs_perm_execute));
+ TEST_CHECK(!b.has_permission(fs_ug_others, fs_perm_read));
+ TEST_CHECK(!b.has_permission(fs_ug_others, fs_perm_write));
+ TEST_CHECK(!b.has_permission(fs_ug_others, fs_perm_execute));
+
+ TEST_CHECK_THROWS(c.has_permission(fs_ug_owner, fs_perm_read), FSError);
+ }
+ } test_fs_entry_permission;
+
+ /**
+ * \test Test FSEntry ctime and mtime methods
+ *
+ * \ingroup grpfilesystem
+ */
+ struct FSEntryTime : TestCase
+ {
+ FSEntryTime() : TestCase("ctime and mtime") {}
+
+ void run()
+ {
+ FSEntry a("fs_entry_TEST_dir");
+ FSEntry b("fs_entry_TEST_dir/no_perms");
+ FSEntry c("fs_entry_TEST_dir/no_such_file");
+
+ TEST_CHECK(a.ctime() <= std::time(NULL));
+ TEST_CHECK((a.mtime() >= a.ctime()) && (a.mtime() <= std::time(NULL)));
+ TEST_CHECK(b.ctime() <= std::time(NULL));
+ TEST_CHECK((b.mtime() >= b.ctime()) && (b.mtime() <= std::time(NULL)));
+
+ TEST_CHECK_THROWS(c.ctime(), FSError);
+ TEST_CHECK_THROWS(c.mtime(), FSError);
+ }
+ } test_fs_entry_time;
+
+ /**
+ * \test Test FSEntry file_size
+ *
+ * \ingroup grpfilesystem
+ */
+ struct FSEntryFileSize : TestCase
+ {
+ FSEntryFileSize() : TestCase("file size") {}
+
+ void run()
+ {
+ FSEntry f("fs_entry_TEST_dir/ten_bytes");
+ FSEntry d("fs_entry_TEST_dir/dir_a");
+ FSEntry e("fs_entry_TEST_dir/no_such_file");
+
+ TEST_CHECK_EQUAL(f.file_size(), 10);
+ TEST_CHECK_THROWS(d.file_size(), FSError);
+ TEST_CHECK_THROWS(e.file_size(), FSError);
+ }
+ } test_fs_entry_size;
+
+ /**
+ * \test Test FSEntry basename and dirname methods
+ *
+ * \ingroup grpfilesystem
+ */
+ struct FSEntryBaseDirName : TestCase
+ {
+ FSEntryBaseDirName() : TestCase("basename and dirname") {}
+
+ void run()
+ {
+ FSEntry a("/foo/bar");
+ FSEntry b("/moo/went/the/cow");
+ FSEntry c("/");
+ FSEntry d(".");
+ FSEntry e("..");
+
+ TEST_CHECK(a.basename() == "bar");
+ TEST_CHECK(stringify(a.dirname()) == "/foo");
+ TEST_CHECK(b.basename() == "cow");
+ TEST_CHECK(stringify(b.dirname()) == "/moo/went/the");
+ TEST_CHECK(c.basename() == "/");
+ TEST_CHECK(stringify(c.dirname()) == "/");
+ TEST_CHECK(d.basename() == ".");
+ TEST_CHECK(stringify(d.dirname()) == ".");
+ TEST_CHECK(e.basename() == "..");
+ TEST_CHECK(stringify(e.dirname()) == "..");
+ }
+ } test_fs_entry_dir_base_name;
+}
+
diff --git a/0.4.0/paludis/util/fs_entry_TEST_cleanup.sh b/0.4.0/paludis/util/fs_entry_TEST_cleanup.sh
new file mode 100755
index 000000000..662b2bb36
--- /dev/null
+++ b/0.4.0/paludis/util/fs_entry_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d fs_entry_TEST_dir ] ; then
+ rm -fr fs_entry_TEST_dir
+else
+ true
+fi
+
diff --git a/0.4.0/paludis/util/fs_entry_TEST_setup.sh b/0.4.0/paludis/util/fs_entry_TEST_setup.sh
new file mode 100755
index 000000000..7d7796bf7
--- /dev/null
+++ b/0.4.0/paludis/util/fs_entry_TEST_setup.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir fs_entry_TEST_dir || exit 2
+cd fs_entry_TEST_dir || exit 3
+mkdir dir_a || exit 4
+ln -s dir_a symlink_to_dir_a || exit 5
+touch dir_a/file_in_a || exit 6
+
+touch all_perms || exit 7
+chmod 777 all_perms || exit 8
+touch no_perms || exit 9
+sleep 1
+echo > no_perms
+chmod 000 no_perms || exit 10
+echo -n '0123456789' > ten_bytes || exit 11
+ln -s dir_a/file_in_a symlink_to_file_in_a || exit 12
diff --git a/0.4.0/paludis/util/instantiation_policy.hh b/0.4.0/paludis/util/instantiation_policy.hh
new file mode 100644
index 000000000..b9a23dd85
--- /dev/null
+++ b/0.4.0/paludis/util/instantiation_policy.hh
@@ -0,0 +1,207 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_INSTANTIATION_POLICY_HH
+#define PALUDIS_GUARD_PALUDIS_INSTANTIATION_POLICY_HH 1
+
+/** \file
+ * InstantiationPolicy patterns.
+ *
+ * \ingroup grpinstance
+ */
+
+namespace paludis
+{
+ /**
+ * Instantiation policies for paludis::InstantiationPolicy.
+ *
+ * \ingroup grpinstance
+ */
+ namespace instantiation_method
+ {
+ /**
+ * Cannot be copie
+ *
+ * \ingroup grpinstanced or assigned to.
+ */
+ struct NonCopyableTag
+ {
+ };
+
+ /**
+ * Cannot be instantia
+ *
+ * \ingroup grpinstanceted.
+ */
+ struct NonInstantiableTag
+ {
+ };
+
+ /**
+ * Single instance cre
+ *
+ * \ingroup grpinstanceated at startup.
+ */
+ struct SingletonAtStartupTag
+ {
+ };
+
+ /**
+ * Single instance cre
+ *
+ * \ingroup grpinstanceated when needed.
+ */
+ struct SingletonAsNeededTag
+ {
+ };
+ }
+
+ /**
+ * InstantiationPolicy is used to specify behaviour of classes that have
+ * something other than the default C++ instantiation behaviour.
+ *
+ * \ingroup grpinstance
+ */
+ template <typename OurType_, typename InstantiationMethodTag_>
+ struct InstantiationPolicy;
+
+ /**
+ * InstantiationPolicy: specialisation for classes that cannot be copied
+ * or assigned to.
+ *
+ * \ingroup grpinstance
+ */
+ template<typename OurType_>
+ class InstantiationPolicy<OurType_, instantiation_method::NonCopyableTag>
+ {
+ private:
+ InstantiationPolicy(const InstantiationPolicy &);
+
+ const InstantiationPolicy & operator= (const InstantiationPolicy &);
+
+ protected:
+ ~InstantiationPolicy()
+ {
+ }
+
+ InstantiationPolicy()
+ {
+ }
+ };
+
+ /**
+ * InstantiationPolicy: specialisation for classes that cannot be created.
+ *
+ * \ingroup grpinstance
+ */
+ template<typename OurType_>
+ class InstantiationPolicy<OurType_, instantiation_method::NonInstantiableTag>
+ {
+ private:
+ InstantiationPolicy(const InstantiationPolicy &);
+
+ const InstantiationPolicy & operator= (const InstantiationPolicy &);
+
+ protected:
+ InstantiationPolicy();
+
+ ~InstantiationPolicy()
+ {
+ }
+ };
+}
+
+#include <paludis/util/counted_ptr.hh>
+
+namespace paludis
+{
+ /**
+ * InstantiationPolicy: specialisation for singleton classes that are
+ * created at startup.
+ *
+ * \ingroup grpinstance
+ */
+ template<typename OurType_>
+ class InstantiationPolicy<OurType_, instantiation_method::SingletonAtStartupTag>
+ {
+ private:
+ InstantiationPolicy(const InstantiationPolicy &);
+
+ const InstantiationPolicy & operator= (const InstantiationPolicy &);
+
+ static CountedPtr<OurType_, count_policy::ExternalCountTag> _instance;
+
+ protected:
+ InstantiationPolicy()
+ {
+ }
+
+ public:
+ /**
+ * Fetch our instance.
+ */
+ static OurType_ * get_instance()
+ {
+ return _instance.raw_pointer();
+ }
+ };
+
+ template <typename OurType_>
+ CountedPtr<OurType_, count_policy::ExternalCountTag>
+ InstantiationPolicy<OurType_, instantiation_method::SingletonAtStartupTag>::_instance(
+ new OurType_);
+
+ /**
+ * InstantiationPolicy: specialisation for singleton classes that are
+ * created as needed.
+ *
+ * \ingroup grpinstance
+ */
+ template<typename OurType_>
+ class InstantiationPolicy<OurType_, instantiation_method::SingletonAsNeededTag>
+ {
+ private:
+ InstantiationPolicy(const InstantiationPolicy &);
+
+ const InstantiationPolicy & operator= (const InstantiationPolicy &);
+
+ static OurType_ * _instance;
+
+ protected:
+ InstantiationPolicy()
+ {
+ }
+
+ public:
+ /**
+ * Fetch our instance.
+ */
+ static OurType_ * get_instance();
+ };
+
+ template<typename OurType_>
+ OurType_ *
+ InstantiationPolicy<OurType_, instantiation_method::SingletonAsNeededTag>::get_instance()
+ {
+ static OurType_ instance;
+ return &instance;
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/instantiation_policy_TEST.cc b/0.4.0/paludis/util/instantiation_policy_TEST.cc
new file mode 100644
index 000000000..ad9cdf25f
--- /dev/null
+++ b/0.4.0/paludis/util/instantiation_policy_TEST.cc
@@ -0,0 +1,136 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/instantiation_policy.hh>
+#include <string>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for instantiation_policy.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace
+{
+ /**
+ * Test class for InstantiationPolicy.
+ *
+ * \ingroup grptestcases
+ */
+ class MyClass :
+ public InstantiationPolicy<MyClass, instantiation_method::SingletonAsNeededTag>
+ {
+ friend class InstantiationPolicy<MyClass, instantiation_method::SingletonAsNeededTag>;
+
+ private:
+ MyClass()
+ {
+ ++instances;
+ }
+
+ public:
+ std::string s;
+
+ static int instances;
+ };
+
+ int MyClass::instances = 0;
+
+ struct MyLoadAtStartupClass :
+ public InstantiationPolicy<MyLoadAtStartupClass, instantiation_method::SingletonAtStartupTag>
+ {
+ friend class InstantiationPolicy<MyLoadAtStartupClass, instantiation_method::SingletonAtStartupTag>;
+
+ private:
+ MyLoadAtStartupClass()
+ {
+ ++instances;
+ }
+
+ public:
+ std::string s;
+
+ static int instances;
+ };
+
+ int MyLoadAtStartupClass::instances = 0;
+}
+
+namespace test_cases
+{
+ /**
+ * \test Test singleton behaviour.
+ *
+ * \ingroup grptestcases
+ */
+ struct SingletonPatternTest : TestCase
+ {
+ SingletonPatternTest() : TestCase("singleton test") { }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(MyClass::instances, 0);
+ TEST_CHECK(0 != MyClass::get_instance());
+ TEST_CHECK_EQUAL(MyClass::instances, 1);
+ TEST_CHECK(MyClass::get_instance() == MyClass::get_instance());
+ TEST_CHECK(MyClass::get_instance()->s.empty());
+ MyClass::get_instance()->s = "foo";
+ TEST_CHECK_EQUAL(MyClass::get_instance()->s, "foo");
+ }
+ } test_singleton_pattern;
+
+ /**
+ * \test Test singleton create at startup behaviour.
+ *
+ * \ingroup grptestcases
+ */
+ struct SingletonPatternCreateAtStartupTest : TestCase
+ {
+ SingletonPatternCreateAtStartupTest() : TestCase("singleton create at startup test") { }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(MyLoadAtStartupClass::instances, 1);
+ TEST_CHECK(0 != MyLoadAtStartupClass::get_instance());
+ TEST_CHECK_EQUAL(MyLoadAtStartupClass::instances, 1);
+ TEST_CHECK(MyLoadAtStartupClass::get_instance() == MyLoadAtStartupClass::get_instance());
+ TEST_CHECK(MyLoadAtStartupClass::get_instance()->s.empty());
+ MyLoadAtStartupClass::get_instance()->s = "foo";
+ TEST_CHECK_EQUAL(MyLoadAtStartupClass::get_instance()->s, "foo");
+ }
+ } test_singleton_pattern_create_at_startup;
+}
+
+
+
diff --git a/0.4.0/paludis/util/is_file_with_extension.cc b/0.4.0/paludis/util/is_file_with_extension.cc
new file mode 100644
index 000000000..13107826a
--- /dev/null
+++ b/0.4.0/paludis/util/is_file_with_extension.cc
@@ -0,0 +1,44 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/is_file_with_extension.hh>
+
+using namespace paludis;
+
+/** \file
+ * Implementation of IsFileWithExtension.
+ *
+ * \ingroup grpfilesystem
+ */
+
+bool
+IsFileWithExtension::operator() (const FSEntry & f) const
+{
+ const std::string filename(f.basename());
+
+ if (filename.length() < _ext.length() + _prefix.length())
+ return false;
+ if (0 != filename.compare(filename.length() - _ext.length(),
+ _ext.length(), _ext))
+ return false;
+ if (0 != filename.compare(0, _prefix.length(), _prefix))
+ return false;
+ return f.is_regular_file() || (f.exists() && f.realpath().is_regular_file());
+}
+
diff --git a/0.4.0/paludis/util/is_file_with_extension.hh b/0.4.0/paludis/util/is_file_with_extension.hh
new file mode 100644
index 000000000..86c2ccb41
--- /dev/null
+++ b/0.4.0/paludis/util/is_file_with_extension.hh
@@ -0,0 +1,75 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_IS_FILE_WITH_EXTENSION_HH
+#define PALUDIS_GUARD_PALUDIS_IS_FILE_WITH_EXTENSION_HH 1
+
+#include <functional>
+#include <paludis/util/fs_entry.hh>
+#include <string>
+
+/** \file
+ * Declarations for the IsFileWithExtension class.
+ *
+ * \ingroup grpfilesystem
+ */
+
+namespace paludis
+{
+ /**
+ * The IsFileWithExtension class is a functor that determines whether an
+ * FSEntry instance is a file with a given extension and (optionally) a
+ * given filename prefix.
+ *
+ * \ingroup grpfilesystem
+ */
+ class IsFileWithExtension :
+ public std::unary_function<bool, FSEntry>
+ {
+ private:
+ const std::string _prefix;
+ const std::string _ext;
+
+ public:
+ /**
+ * Constructor.
+ */
+ IsFileWithExtension(const std::string & ext) :
+ _prefix(""),
+ _ext(ext)
+ {
+ }
+
+ /**
+ * Constructor.
+ */
+ IsFileWithExtension(const std::string & prefix, const std::string & ext) :
+ _prefix(prefix),
+ _ext(ext)
+ {
+ }
+
+ /**
+ * Operator.
+ */
+ bool operator() (const FSEntry & f) const;
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/is_file_with_extension_TEST.cc b/0.4.0/paludis/util/is_file_with_extension_TEST.cc
new file mode 100644
index 000000000..a60c2243c
--- /dev/null
+++ b/0.4.0/paludis/util/is_file_with_extension_TEST.cc
@@ -0,0 +1,94 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Mark Loeser <halcy0n@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <algorithm>
+#include <paludis/util/is_file_with_extension.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+
+/** \file
+ * Test cases for IsFileWithExtension.
+ *
+ * \ingroup grptestcases
+ */
+
+using namespace paludis;
+using namespace test;
+
+namespace test_cases
+{
+ /**
+ * \test Test IsFileWithExtension.
+ *
+ * \ingroup grptestcases
+ */
+ struct IsFileWithExtensionTest : TestCase
+ {
+ IsFileWithExtensionTest() : TestCase("is file with extension") { }
+
+ void run()
+ {
+ IsFileWithExtension a("foo");
+ IsFileWithExtension b("goat");
+
+ FSEntry c("teh.foo");
+ FSEntry d("is_file_with_extension_TEST_file.goat");
+
+ TEST_CHECK(d.exists());
+
+ TEST_CHECK( !a(c) );
+ TEST_CHECK( !a(d) );
+ TEST_CHECK( !b(c) );
+ TEST_CHECK( b(d) );
+
+ }
+ } test_is_file_with_extension;
+
+ /**
+ * \test Test IsFileWithExtension with a prefix.
+ *
+ * \ingroup grptestcases
+ */
+ struct IsFileWithExtensionPrefixTest : TestCase
+ {
+ IsFileWithExtensionPrefixTest() : TestCase("is file with extension (with prefix)") { }
+
+ void run()
+ {
+ IsFileWithExtension a("teh","foo");
+ IsFileWithExtension b("is", "goat");
+ IsFileWithExtension c("with", "goat");
+
+ FSEntry d("teh.foo");
+ FSEntry e("is_file_with_extension_TEST_file.goat");
+
+ TEST_CHECK(e.exists());
+
+ TEST_CHECK( !a(d) );
+ TEST_CHECK( !a(e) );
+ TEST_CHECK( !b(d) );
+ TEST_CHECK( b(e) );
+ TEST_CHECK( !c(e) );
+ }
+ } test_is_file_with_extension_prefix;
+
+
+}
+
diff --git a/0.4.0/paludis/util/is_file_with_extension_TEST_cleanup.sh b/0.4.0/paludis/util/is_file_with_extension_TEST_cleanup.sh
new file mode 100755
index 000000000..986933aae
--- /dev/null
+++ b/0.4.0/paludis/util/is_file_with_extension_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -f "is_file_with_extension_TEST_file.goat" ] ; then
+ rm -f "is_file_with_extension_TEST_file.goat"
+else
+ true
+fi
+
diff --git a/0.4.0/paludis/util/is_file_with_extension_TEST_setup.sh b/0.4.0/paludis/util/is_file_with_extension_TEST_setup.sh
new file mode 100755
index 000000000..bdfacbf64
--- /dev/null
+++ b/0.4.0/paludis/util/is_file_with_extension_TEST_setup.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+touch is_file_with_extension_TEST_file.goat || exit 2
+
+
diff --git a/0.4.0/paludis/util/iterator.hh b/0.4.0/paludis/util/iterator.hh
new file mode 100644
index 000000000..55b721df6
--- /dev/null
+++ b/0.4.0/paludis/util/iterator.hh
@@ -0,0 +1,536 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_INDIRECT_ITERATOR_HH
+#define PALUDIS_GUARD_PALUDIS_INDIRECT_ITERATOR_HH 1
+
+#include <iterator>
+#include <paludis/util/comparison_policy.hh>
+#include <paludis/util/instantiation_policy.hh>
+
+/** \file
+ * Declarations for various iterator helpers.
+ *
+ * \ingroup grpiterators
+ */
+
+namespace paludis
+{
+ /**
+ * Return a new iterator pointing to the item after i.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename T_>
+ T_ next(const T_ & i)
+ {
+ T_ result(i);
+ return ++result;
+ }
+
+ /**
+ * Return a new iterator pointing to the item before i.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename T_>
+ T_ previous(const T_ & i)
+ {
+ T_ result(i);
+ return --result;
+ }
+
+ template <typename Iter_, typename Value_>
+ class IndirectIterator;
+
+ namespace
+ {
+ /**
+ * Determine the comparison class to use for IndirectIterator.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename IterCategory_, typename Iter_, typename Value_>
+ struct Comparisons
+ {
+ /**
+ * Default to providing == and !=.
+ */
+ typedef ComparisonPolicy<IndirectIterator<Iter_, Value_>,
+ comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<Iter_> > Type;
+ };
+
+ /**
+ * Determine the comparison class to use for IndirectIterator
+ * (specialisation for random access iterators).
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Value_>
+ struct Comparisons<std::random_access_iterator_tag, Iter_, Value_>
+ {
+ /**
+ * Provide the full range of comparison operators.
+ */
+ typedef ComparisonPolicy<IndirectIterator<Iter_, Value_>,
+ comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<Iter_> > Type;
+ };
+ }
+
+ /**
+ * An IndirectIterator is an iterator adapter that does one additional level
+ * of dereferencing.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Value_>
+ class IndirectIterator : public std::iterator<typename std::iterator_traits<Iter_>::iterator_category, Value_>,
+ public Comparisons<typename std::iterator_traits<Iter_>::iterator_category,
+ Iter_, Value_>::Type
+ {
+ private:
+ Iter_ _i;
+
+ public:
+ /**
+ * Constructor, from a base iterator.
+ */
+ IndirectIterator(const Iter_ & i) :
+ Comparisons<typename std::iterator_traits<Iter_>::iterator_category, Iter_, Value_>::Type(
+ &IndirectIterator<Iter_, Value_>::_i),
+ _i(i)
+ {
+ }
+
+ /**
+ * Copy constructor.
+ */
+ IndirectIterator(const IndirectIterator & other) :
+ Comparisons<typename std::iterator_traits<Iter_>::iterator_category, Iter_, Value_>::Type(
+ &IndirectIterator<Iter_, Value_>::_i),
+ _i(other._i)
+ {
+ }
+
+ /**
+ * Assignment.
+ */
+ const IndirectIterator & operator= (const IndirectIterator & other)
+ {
+ _i = other._i;
+ return *this;
+ }
+
+ /**
+ * Dereference.
+ */
+ Value_ & operator*()
+ {
+ return **_i;
+ }
+
+ /**
+ * Dereference arrow.
+ */
+ Value_ * operator->()
+ {
+ return &**_i;
+ }
+
+ /**
+ * Dereference, const.
+ */
+ const Value_ & operator*() const
+ {
+ return **_i;
+ }
+
+ /**
+ * Dereference arrow, const.
+ */
+ const Value_ * operator->() const
+ {
+ return &**_i;
+ }
+
+ /**
+ * Increment.
+ */
+ IndirectIterator & operator++ ()
+ {
+ ++_i;
+ return *this;
+ }
+
+ /**
+ * Increment.
+ */
+ IndirectIterator operator++ (int)
+ {
+ IndirectIterator tmp(*this);
+ ++_i;
+ return tmp;
+ }
+ };
+
+ /**
+ * Convenience constructor for an IndirectIterator.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Value_, typename Iter_>
+ IndirectIterator<Iter_, Value_> indirect_iterator(const Iter_ & i)
+ {
+ return IndirectIterator<Iter_, Value_>(i);
+ }
+
+ /**
+ * A FilterInsertIterator is an insert iterator that only performs an insert
+ * if a particular predicate function returns true for the object to be
+ * inserted.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Pred_>
+ class FilterInsertIterator :
+ public std::iterator<typename std::iterator_traits<Iter_>::iterator_category, void, void, void, void>
+ {
+ private:
+ Iter_ _i;
+ Pred_ _p;
+
+ public:
+ /**
+ * Fake a container_type for use with other iterator adapters.
+ */
+ typedef typename Iter_::container_type container_type;
+
+ /**
+ * Constructor, from an iterator.
+ */
+ FilterInsertIterator(const Iter_ & i, const Pred_ & p) :
+ _i(i),
+ _p(p)
+ {
+ }
+
+ /**
+ * Copy constructor.
+ */
+ FilterInsertIterator(const FilterInsertIterator & other) :
+ _i(other._i),
+ _p(other._p)
+ {
+ }
+
+ /**
+ * Assignment.
+ */
+ template <typename T_>
+ const FilterInsertIterator & operator= (const T_ value)
+ {
+ if (_p(value))
+ *_i = value;
+ return *this;
+ }
+
+ /**
+ * Destructor.
+ */
+ ~FilterInsertIterator();
+
+ /**
+ * Dereference.
+ */
+ FilterInsertIterator & operator* ()
+ {
+ return *this;
+ }
+
+ /**
+ * Dereference arrow.
+ */
+ FilterInsertIterator * operator-> ()
+ {
+ return this;
+ }
+
+ /**
+ * Increment.
+ */
+ FilterInsertIterator & operator++ ()
+ {
+ return *this;
+ }
+
+ /**
+ * Increment.
+ */
+ FilterInsertIterator & operator++ (int)
+ {
+ return *this;
+ }
+ };
+
+ template <typename Iter_, typename Pred_>
+ FilterInsertIterator<Iter_, Pred_>::~FilterInsertIterator()
+ {
+ }
+
+ /**
+ * Convenience function: make a FilterInsertIterator.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Pred_>
+ FilterInsertIterator<Iter_, Pred_> filter_inserter(
+ const Iter_ & i, const Pred_ & p)
+ {
+ return FilterInsertIterator<Iter_, Pred_>(i, p);
+ }
+
+ /**
+ * A TransformInsertIterator is an insert iterator that calls some function
+ * upon an item before inserting it.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Trans_>
+ class TransformInsertIterator :
+ public std::iterator<typename std::iterator_traits<Iter_>::iterator_category, void, void, void, void>
+ {
+ private:
+ Iter_ _i;
+ Trans_ _t;
+
+ public:
+ /**
+ * Fake a container_type entry to allow a TransformInsertIterator to
+ * work with other iterator adapters.
+ */
+ struct container_type
+ {
+ /// Our value type.
+ typedef typename Trans_::argument_type value_type;
+ };
+
+ /**
+ * Constructor, from an iterator.
+ */
+ TransformInsertIterator(const Iter_ & i, const Trans_ & t = Trans_()) :
+ _i(i),
+ _t(t)
+ {
+ }
+
+ /**
+ * Copy constructor.
+ */
+ TransformInsertIterator(const TransformInsertIterator & other) :
+ _i(other._i),
+ _t(other._t)
+ {
+ }
+
+ /**
+ * Assignment.
+ */
+ template <typename T_>
+ const TransformInsertIterator & operator= (const T_ value)
+ {
+ *_i = _t(value);
+ return *this;
+ }
+
+ /**
+ * Dereference.
+ */
+ TransformInsertIterator & operator* ()
+ {
+ return *this;
+ }
+
+ /**
+ * Dereference arrow.
+ */
+ TransformInsertIterator * operator-> ()
+ {
+ return this;
+ }
+
+ /**
+ * Increment.
+ */
+ TransformInsertIterator & operator++ ()
+ {
+ return *this;
+ }
+
+ /**
+ * Increment.
+ */
+ TransformInsertIterator & operator++ (int)
+ {
+ return *this;
+ }
+
+ };
+
+ /**
+ * Convenience function: make a TransformInsertIterator.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Trans_>
+ TransformInsertIterator<Iter_, Trans_> transform_inserter(
+ const Iter_ & i, const Trans_ & t)
+ {
+ return TransformInsertIterator<Iter_, Trans_>(i, t);
+ }
+
+ /**
+ * Convenience class: select the first item of a pair.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename A_, typename B_>
+ struct SelectFirst :
+ std::unary_function<A_, std::pair<A_, B_> >
+ {
+ /// Carry out the selection.
+ A_ operator() (const std::pair<A_, B_> & p) const
+ {
+ return p.first;
+ }
+ };
+
+ /**
+ * Convenience class: select the second item of a pair.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename A_, typename B_>
+ struct SelectSecond :
+ std::unary_function<A_, std::pair<A_, B_> >
+ {
+ /// Carry out the selection.
+ A_ operator() (const std::pair<A_, B_> & p) const
+ {
+ return p.second;
+ }
+ };
+
+ /**
+ * A CreateInsertIterator is an insert iterator that creates an object of
+ * the specified type using the provided value.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Iter_, typename Type_>
+ class CreateInsertIterator :
+ public std::iterator<typename std::iterator_traits<Iter_>::iterator_category, void, void, void, void>
+ {
+ private:
+ Iter_ _i;
+
+ public:
+ /**
+ * Fake a container_type to allow us to work with other iterator
+ * adapters.
+ */
+ struct container_type
+ {
+ /// Our faked item type.
+ typedef Type_ value_type;
+ };
+
+ /**
+ * Constructor, from an iterator.
+ */
+ CreateInsertIterator(const Iter_ & i) :
+ _i(i)
+ {
+ }
+
+ /**
+ * Copy constructor.
+ */
+ CreateInsertIterator(const CreateInsertIterator & other) :
+ _i(other._i)
+ {
+ }
+
+ /**
+ * Assignment.
+ */
+ template <typename T_>
+ const CreateInsertIterator & operator= (const T_ value)
+ {
+ *_i = Type_(value);
+ return *this;
+ }
+
+ /**
+ * Dereference.
+ */
+ CreateInsertIterator & operator* ()
+ {
+ return *this;
+ }
+
+ /**
+ * Dereference arrow.
+ */
+ CreateInsertIterator * operator-> ()
+ {
+ return this;
+ }
+
+ /**
+ * Increment.
+ */
+ CreateInsertIterator & operator++ ()
+ {
+ return *this;
+ }
+
+ /**
+ * Increment.
+ */
+ CreateInsertIterator & operator++ (int)
+ {
+ return *this;
+ }
+ };
+
+ /**
+ * Convenience function: make a CreateInsertIterator.
+ *
+ * \ingroup grpiterators
+ */
+ template <typename Type_, typename Iter_>
+ CreateInsertIterator<Iter_, Type_> create_inserter(const Iter_ & i)
+ {
+ return CreateInsertIterator<Iter_, Type_>(i);
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/iterator_TEST.cc b/0.4.0/paludis/util/iterator_TEST.cc
new file mode 100644
index 000000000..06e7d7067
--- /dev/null
+++ b/0.4.0/paludis/util/iterator_TEST.cc
@@ -0,0 +1,339 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <algorithm>
+#include <list>
+#include <paludis/util/counted_ptr.hh>
+#include <paludis/util/deleter.hh>
+#include <paludis/util/iterator.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+#include <set>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for iterator utilities.
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test IndirectIterator over a vector of CountedPtr of int.
+ *
+ * \ingroup grptestcases
+ */
+ struct IndirectIteratorVecCPIntTest : TestCase
+ {
+ IndirectIteratorVecCPIntTest() : TestCase("vector<CountedPtr<int> >") { }
+
+ void run()
+ {
+ std::vector<CountedPtr<int, count_policy::ExternalCountTag> > v;
+ v.push_back(CountedPtr<int, count_policy::ExternalCountTag>(new int(5)));
+ v.push_back(CountedPtr<int, count_policy::ExternalCountTag>(new int(10)));
+ IndirectIterator<std::vector<CountedPtr<int,
+ count_policy::ExternalCountTag> >::iterator, int> vi(v.begin()), vi_end(v.end());
+ TEST_CHECK(vi != vi_end);
+ TEST_CHECK(vi < vi_end);
+ TEST_CHECK(! (vi > vi_end));
+ TEST_CHECK_EQUAL(*vi, 5);
+ TEST_CHECK(++vi != vi_end);
+ TEST_CHECK(vi < vi_end);
+ TEST_CHECK(! (vi > vi_end));
+ TEST_CHECK_EQUAL(*vi, 10);
+ TEST_CHECK(++vi == vi_end);
+ }
+ } test_indirect_iterator_vec_cp_int;
+
+ /**
+ * \test Test IndirectIterator over a list of CountedPtr of int.
+ *
+ * \ingroup grptestcases
+ */
+ struct IndirectIteratorListCPIntTest : TestCase
+ {
+ IndirectIteratorListCPIntTest() : TestCase("list<CountedPtr<int> >") { }
+
+ void run()
+ {
+ std::list<CountedPtr<int, count_policy::ExternalCountTag> > v;
+ v.push_back(CountedPtr<int, count_policy::ExternalCountTag>(new int(5)));
+ v.push_back(CountedPtr<int, count_policy::ExternalCountTag>(new int(10)));
+ IndirectIterator<std::list<CountedPtr<int,
+ count_policy::ExternalCountTag> >::iterator, int> vi(v.begin()), vi_end(v.end());
+ TEST_CHECK(vi != vi_end);
+ TEST_CHECK_EQUAL(*vi, 5);
+ TEST_CHECK(++vi != vi_end);
+ TEST_CHECK_EQUAL(*vi, 10);
+ TEST_CHECK(++vi == vi_end);
+ }
+ } test_indirect_iterator_list_cp_int;
+
+ /**
+ * \test Test IndirectIterator over a vector of int *.
+ *
+ * \ingroup grptestcases
+ */
+ struct IndirectIteratorVecPIntTest : TestCase
+ {
+ IndirectIteratorVecPIntTest() : TestCase("vector<int *>") { }
+
+ void run()
+ {
+ std::vector<int *> v;
+ v.push_back(new int(5));
+ v.push_back(new int(10));
+ IndirectIterator<std::vector<int *>::iterator, int> vi(v.begin()), vi_end(v.end());
+ TEST_CHECK(vi != vi_end);
+ TEST_CHECK(vi < vi_end);
+ TEST_CHECK(! (vi > vi_end));
+ TEST_CHECK_EQUAL(*vi, 5);
+ TEST_CHECK(++vi != vi_end);
+ TEST_CHECK(vi < vi_end);
+ TEST_CHECK(! (vi > vi_end));
+ TEST_CHECK_EQUAL(*vi, 10);
+ TEST_CHECK(++vi == vi_end);
+
+ std::for_each(v.begin(), v.end(), Deleter());
+ }
+ } test_indirect_iterator_vec_p_int;
+
+ /**
+ * \test Test IndirectIterator over a list of int *.
+ *
+ * \ingroup grptestcases
+ */
+ struct IndirectIteratorListPIntTest : TestCase
+ {
+ IndirectIteratorListPIntTest() : TestCase("list<CountedPtr<int *>") { }
+
+ void run()
+ {
+ std::list<int *> v;
+ v.push_back(new int(5));
+ v.push_back(new int(10));
+ IndirectIterator<std::list<int *>::iterator, int> vi(v.begin()), vi_end(v.end());
+ TEST_CHECK(vi != vi_end);
+ TEST_CHECK_EQUAL(*vi, 5);
+ TEST_CHECK(++vi != vi_end);
+ TEST_CHECK_EQUAL(*vi, 10);
+ TEST_CHECK(++vi == vi_end);
+
+ std::for_each(v.begin(), v.end(), Deleter());
+ }
+ } test_indirect_iterator_list_p_int;
+}
+
+#ifndef DOXYGEN
+struct Counter
+{
+ int n;
+
+ Counter() :
+ n(0)
+ {
+ }
+
+ int operator() ()
+ {
+ return n++;
+ }
+};
+
+int is_even(const int & v)
+{
+ return ! (v & 1);
+}
+#endif
+
+
+namespace test_cases
+{
+ /**
+ * \test Test FilterInsertIterator.
+ *
+ * \ingroup grptestcases
+ */
+ struct FilterInsertIteratorTest : TestCase
+ {
+ FilterInsertIteratorTest() : TestCase("filter insert iterator") { }
+
+ void run()
+ {
+ std::set<int> v;
+ std::generate_n(filter_inserter(std::inserter(v, v.begin()), std::ptr_fun(&is_even)),
+ 5, Counter());
+ TEST_CHECK_EQUAL(v.size(), 3);
+ for (int n = 0 ; n < 5 ; ++n)
+ {
+ TestMessageSuffix s("n=" + stringify(n));
+ if (is_even(n))
+ {
+ TEST_CHECK(v.end() != v.find(n));
+ TEST_CHECK_EQUAL(*v.find(n), n);
+ }
+ else
+ TEST_CHECK(v.end() == v.find(n));
+ }
+ }
+ } test_filter_insert_iterator;
+
+ /**
+ * \test Test iterator_utilities next()
+ *
+ * \ingroup grptestcases
+ */
+ struct IteratorNextTest : public TestCase
+ {
+ IteratorNextTest() : TestCase("iterator next()") { }
+
+ void run()
+ {
+ std::vector<int> v;
+ v.push_back(1);
+ v.push_back(2);
+ std::vector<int>::iterator iter(v.begin());
+
+ TEST_CHECK(*(next(iter)) == 2);
+ TEST_CHECK(next(next(iter)) == v.end());
+ iter = next(iter);
+ TEST_CHECK(++iter == v.end());
+ }
+ } test_iterator_next;
+
+ /**
+ * \test Test iterator_utilities previous()
+ *
+ * \ingroup grptestcases
+ */
+ struct IteratorpreviousTest : public TestCase
+ {
+ IteratorpreviousTest() : TestCase("iterator previous()") { }
+
+ void run()
+ {
+ std::vector<int> v;
+ v.push_back(1);
+ v.push_back(2);
+ std::vector<int>::iterator iter(v.end());
+
+ TEST_CHECK(*(previous(iter)) == 2);
+ TEST_CHECK(previous(previous(iter)) == v.begin());
+ iter = previous(iter);
+ TEST_CHECK(--iter == v.begin());
+ }
+ } test_iterator_previous;
+}
+
+#ifndef DOXYGEN
+int f(const int & v)
+{
+ return -v;
+}
+#endif
+
+namespace test_cases
+{
+ /**
+ * \test Test TransformInsertIterator.
+ *
+ * \ingroup grptestcases
+ */
+ struct TransformInsertIteratorTest : TestCase
+ {
+ TransformInsertIteratorTest() : TestCase("transform insert iterator") { }
+
+ void run()
+ {
+ std::vector<int> v;
+ std::generate_n(transform_inserter(std::back_inserter(v), std::ptr_fun(&f)),
+ 5, Counter());
+ TEST_CHECK_EQUAL(v.size(), 5);
+ for (int n = 0 ; n < 5 ; ++n)
+ {
+ TestMessageSuffix s("n=" + stringify(n));
+ TEST_CHECK_EQUAL(v.at(n), -n);
+ }
+ }
+ } test_transform_insert_iterator;
+
+ /**
+ * \test Test SelectFirst and SelectSecond.
+ *
+ * \ingroup grptestcases
+ */
+ struct SimpleSelectPairTest : TestCase
+ {
+ SimpleSelectPairTest() : TestCase("Simple SelectFirst and SelectSecond") {}
+
+ void run()
+ {
+ std::pair<int,int> p(1,2);
+ SelectFirst<int,int> f;
+ SelectSecond<int,int> s;
+
+ TEST_CHECK(f(p) == 1);
+ TEST_CHECK(s(p) == 2);
+ }
+ } test_select_pair;
+}
+
+#ifndef DOXYGEN
+struct C
+{
+ std::string s;
+
+ explicit C(const std::string & ss) :
+ s(ss)
+ {
+ }
+};
+#endif
+
+namespace test_cases
+{
+ /**
+ * \test Test create_inserter.
+ *
+ * \ingroup grptestcases
+ */
+ struct CreateInsertIteratorTest : TestCase
+ {
+ CreateInsertIteratorTest() : TestCase("create insert iterator") { }
+
+ void run()
+ {
+ std::vector<std::string> v;
+ v.push_back("one");
+ v.push_back("two");
+
+ std::vector<C> vv;
+ std::copy(v.begin(), v.end(), create_inserter<C>(std::back_inserter(vv)));
+
+ TEST_CHECK_EQUAL(vv.size(), 2);
+ TEST_CHECK_EQUAL(vv.at(0).s, "one");
+ TEST_CHECK_EQUAL(vv.at(1).s, "two");
+ }
+ } test_create_insert_iterator;
+}
diff --git a/0.4.0/paludis/util/join.hh b/0.4.0/paludis/util/join.hh
new file mode 100644
index 000000000..a69b43235
--- /dev/null
+++ b/0.4.0/paludis/util/join.hh
@@ -0,0 +1,67 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_JOIN_HH
+#define PALUDIS_GUARD_PALUDIS_JOIN_HH 1
+
+#include <paludis/util/stringify.hh>
+#include <string>
+
+/** \file
+ * Declarations for the join function.
+ *
+ * \ingroup grpjoin
+ */
+
+namespace paludis
+{
+ /**
+ * Join together the items from i to end using joiner.
+ *
+ * \ingroup grpjoin
+ */
+ template <typename I_, typename T_>
+ T_ join(I_ i, I_ end, const T_ & joiner)
+ {
+ T_ result;
+ if (i != end)
+ while (true)
+ {
+ result += stringify(*i);
+ if (++i == end)
+ break;
+ result += joiner;
+ }
+ return result;
+ }
+
+ /**
+ * Convenience alternative join allowing a char * to be used for a
+ * string.
+ *
+ * \ingroup grpjoin
+ */
+ template <typename I_>
+ std::string join(I_ begin, const I_ end, const char * const t)
+ {
+ return join(begin, end, std::string(t));
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/join_TEST.cc b/0.4.0/paludis/util/join_TEST.cc
new file mode 100644
index 000000000..8233e203d
--- /dev/null
+++ b/0.4.0/paludis/util/join_TEST.cc
@@ -0,0 +1,102 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <list>
+#include <paludis/util/join.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Test cases for join.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test join on a vector.
+ *
+ * \ingroup grptestcases
+ */
+ struct JoinVectorTest : TestCase
+ {
+ JoinVectorTest() : TestCase("join vector") { }
+
+ void run()
+ {
+ std::vector<std::string> v;
+ v.push_back("one");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "/"), "one");
+ v.push_back("two");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "/"), "one/two");
+ v.push_back("three");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "/"), "one/two/three");
+ }
+ } test_join_vector;
+
+ /**
+ * \test Test join on a list.
+ *
+ * \ingroup grptestcases
+ */
+ struct JoinListTest : TestCase
+ {
+ JoinListTest() : TestCase("join list") { }
+
+ void run()
+ {
+ std::list<std::string> v;
+ v.push_back("one");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "/"), "one");
+ v.push_back("two");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "/"), "one/two");
+ v.push_back("three");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "/"), "one/two/three");
+ }
+ } test_join_list;
+
+ /**
+ * \test Test join with empty things.
+ *
+ * \ingroup grptestcases
+ */
+ struct JoinEmptyTest : TestCase
+ {
+ JoinEmptyTest() : TestCase("join empty") { }
+
+ void run()
+ {
+ std::list<std::string> v;
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), ""), "");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "*"), "");
+ v.push_back("");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), ""), "");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "*"), "");
+ v.push_back("");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), ""), "");
+ TEST_CHECK_EQUAL(join(v.begin(), v.end(), "*"), "*");
+ }
+ } test_join_empty;
+}
+
diff --git a/0.4.0/paludis/util/log.cc b/0.4.0/paludis/util/log.cc
new file mode 100644
index 000000000..8ef05100d
--- /dev/null
+++ b/0.4.0/paludis/util/log.cc
@@ -0,0 +1,151 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <paludis/util/log.hh>
+
+/** \file
+ * Implementation for Log.
+ *
+ * \ingroup grplog
+ */
+
+using namespace paludis;
+
+namespace paludis
+{
+ /**
+ * Implementation data for Log.
+ *
+ * \ingroup grplog
+ */
+ template<>
+ struct Implementation<Log> :
+ InternalCounted<Implementation<Log> >
+ {
+ /// Current log level
+ LogLevel log_level;
+
+ /// Current output stream
+ std::ostream * stream;
+
+ /// Program name
+ std::string program_name;
+ };
+}
+
+Log::Log() :
+ PrivateImplementationPattern<Log>(new Implementation<Log>)
+{
+ _imp->log_level = initial_ll;
+ _imp->stream = &std::cerr;
+ _imp->program_name = "paludis";
+}
+
+Log::~Log()
+{
+}
+
+void
+Log::set_log_level(const LogLevel l)
+{
+ _imp->log_level = l;
+}
+
+LogLevel
+Log::log_level() const
+{
+ return _imp->log_level;
+}
+
+void
+Log::message(const LogLevel l, const LogContext c, const std::string & s)
+{
+ if (l >= _imp->log_level)
+ {
+ *_imp->stream << _imp->program_name << "@" << ::time(0) << ": ";
+ do
+ {
+ switch (l)
+ {
+ case ll_debug:
+ *_imp->stream << "[DEBUG] ";
+ continue;
+
+ case ll_qa:
+ *_imp->stream << "[QA] ";
+ continue;
+
+ case ll_warning:
+ *_imp->stream << "[WARNING] ";
+ continue;
+
+ case ll_silent:
+ throw InternalError(PALUDIS_HERE, "ll_silent used for a message");
+
+ case last_ll:
+ break;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Bad value for log_level");
+
+ } while (false);
+
+ if (lc_context == c)
+ *_imp->stream << Context::backtrace(" ") << s << std::endl;
+ else
+ *_imp->stream << s << std::endl;
+ }
+}
+
+void
+Log::set_log_stream(std::ostream * const s)
+{
+ _imp->stream = s;
+}
+
+std::string
+Log::log_level_string() const
+{
+ switch (Log::get_instance()->log_level())
+ {
+ case ll_qa:
+ return "qa";
+
+ case ll_warning:
+ return "warning";
+
+ case ll_debug:
+ return "debug";
+
+ case ll_silent:
+ return "silent";
+
+ case last_ll:
+ ;
+ };
+
+ throw InternalError(PALUDIS_HERE, "Bad log level");
+}
+
+void
+Log::set_program_name(const std::string & s)
+{
+ _imp->program_name = s;
+}
diff --git a/0.4.0/paludis/util/log.hh b/0.4.0/paludis/util/log.hh
new file mode 100644
index 000000000..3bd859b80
--- /dev/null
+++ b/0.4.0/paludis/util/log.hh
@@ -0,0 +1,121 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_LOG_HH
+#define PALUDIS_GUARD_PALUDIS_LOG_HH 1
+
+#include <iosfwd>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+/** \file
+ * Declarations for Log and related classes.
+ *
+ * \ingroup grplog
+ */
+
+namespace paludis
+{
+ /**
+ * Specifies the level of a log message.
+ *
+ * Keep this in order. When deciding whether to display a message, Log
+ * uses message log level >= current log level, so it's important that
+ * least critical levels have lower numeric values.
+ *
+ * When modifying this, you will probably also want to take a look at
+ * ebuild/echo_functions.bash and the command_line source files.
+ *
+ * \ingroup grplog
+ */
+ enum LogLevel
+ {
+ ll_debug, ///< Debug message
+ ll_qa, ///< QA messages
+ ll_warning, ///< Warning message
+ ll_silent, ///< Silent (for set_log_level)
+ last_ll, ///< Number of items
+ initial_ll = ll_debug ///< Initial value
+ };
+
+ /**
+ * Specifies whether a log message has context.
+ *
+ * \ingroup grplog
+ */
+ enum LogContext
+ {
+ lc_no_context,
+ lc_context,
+ last_lc
+ };
+
+ /**
+ * Singleton class that handles log messages.
+ *
+ * \ingroup grplog
+ */
+ class Log :
+ public InstantiationPolicy<Log, instantiation_method::SingletonAsNeededTag>,
+ private PrivateImplementationPattern<Log>
+ {
+ friend class InstantiationPolicy<Log, instantiation_method::SingletonAsNeededTag>;
+
+ private:
+ Log();
+
+ public:
+ /**
+ * Destructor, to be called only by our InstantiationPolicy.
+ */
+ ~Log();
+
+ /**
+ * Only display messages of at least this level.
+ */
+ void set_log_level(const LogLevel);
+
+ /**
+ * Fetch the current log level.
+ */
+ LogLevel log_level() const;
+
+ /**
+ * Log a message at the specified level.
+ */
+ void message(const LogLevel, const LogContext, const std::string &);
+
+ /**
+ * Change the log stream.
+ */
+ void set_log_stream(std::ostream * const);
+
+ /**
+ * Log level, as a string.
+ */
+ std::string log_level_string() const;
+
+ /**
+ * Set our program name.
+ */
+ void set_program_name(const std::string &);
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/log_TEST.cc b/0.4.0/paludis/util/log_TEST.cc
new file mode 100644
index 000000000..ed8fc0a74
--- /dev/null
+++ b/0.4.0/paludis/util/log_TEST.cc
@@ -0,0 +1,74 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/log.hh>
+#include <sstream>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+/** \file
+ * Test cases for Log.
+ *
+ * \ingroup grptestcases
+ */
+
+using namespace paludis;
+using namespace test;
+
+namespace test_cases
+{
+ /**
+ * \test Test Log.
+ *
+ * \ingroup grptestcases
+ */
+ struct LogTest : TestCase
+ {
+ LogTest() : TestCase("log") { }
+
+ void run()
+ {
+ TEST_CHECK(Log::get_instance());
+ TEST_CHECK(Log::get_instance() == Log::get_instance());
+
+ std::stringstream s;
+ Log::get_instance()->set_log_stream(&s);
+ Log::get_instance()->set_log_level(ll_debug);
+
+ TEST_CHECK(s.str().empty());
+ Log::get_instance()->message(ll_debug, lc_no_context, "one");
+ TEST_CHECK(! s.str().empty());
+ TEST_CHECK(std::string::npos != s.str().find("one"));
+
+ std::stringstream t;
+ Log::get_instance()->set_log_stream(&t);
+ TEST_CHECK(t.str().empty());
+
+ Log::get_instance()->set_log_level(ll_warning);
+ Log::get_instance()->message(ll_debug, lc_no_context, "two");
+ TEST_CHECK(t.str().empty());
+ Log::get_instance()->message(ll_warning, lc_no_context, "three");
+ TEST_CHECK(! t.str().empty());
+ TEST_CHECK(std::string::npos == t.str().find("one"));
+ TEST_CHECK(std::string::npos == t.str().find("two"));
+ TEST_CHECK(std::string::npos != t.str().find("three"));
+ }
+ } test_log;
+}
+
diff --git a/0.4.0/paludis/util/private_implementation_pattern.hh b/0.4.0/paludis/util/private_implementation_pattern.hh
new file mode 100644
index 000000000..a73a95c66
--- /dev/null
+++ b/0.4.0/paludis/util/private_implementation_pattern.hh
@@ -0,0 +1,70 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_PRIVATE_IMPLEMENTATION_PATTERN_HH
+#define PALUDIS_GUARD_PALUDIS_PRIVATE_IMPLEMENTATION_PATTERN_HH 1
+
+#include <paludis/util/counted_ptr.hh>
+#include <paludis/util/instantiation_policy.hh>
+
+/** \file
+ * Declarations for the PrivateImplementationPattern pattern.
+ *
+ * \ingroup grppimp
+ */
+
+namespace paludis
+{
+ /**
+ * Private implementation data, to be specialised for any class that
+ * uses PrivateImplementationPattern.
+ *
+ * \ingroup grppimp
+ */
+ template <typename C_>
+ struct Implementation;
+
+ /**
+ * A class descended from PrivateImplementationPattern has an associated
+ * Implementation instance.
+ *
+ * \ingroup grppimp
+ */
+ template <typename C_>
+ class PrivateImplementationPattern :
+ private InstantiationPolicy<PrivateImplementationPattern<C_>, instantiation_method::NonCopyableTag>
+ {
+ protected:
+ /**
+ * Pointer to our implementation data.
+ */
+ CountedPtr<Implementation<C_>, count_policy::InternalCountTag> _imp;
+
+ public:
+ /**
+ * Constructor.
+ */
+ explicit PrivateImplementationPattern(Implementation<C_> * i) :
+ _imp(i)
+ {
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/pstream.cc b/0.4.0/paludis/util/pstream.cc
new file mode 100644
index 000000000..940b4d200
--- /dev/null
+++ b/0.4.0/paludis/util/pstream.cc
@@ -0,0 +1,99 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <cstring>
+#include <errno.h>
+#include <paludis/util/log.hh>
+#include <paludis/util/pstream.hh>
+
+/** \file
+ * Implementation for PStream.
+ *
+ * \ingroup grpsystem
+ */
+
+using namespace paludis;
+
+PStreamError::PStreamError(const std::string & message) throw () :
+ Exception(message)
+{
+}
+
+PStreamInBuf::int_type
+PStreamInBuf::underflow()
+{
+ if (0 == fd)
+ return EOF;
+
+ if (gptr() < egptr())
+ return *gptr();
+
+ int num_putback = gptr() - eback();
+ if (num_putback > putback_size)
+ num_putback = putback_size;
+ std::memmove(buffer + putback_size - num_putback,
+ gptr() - num_putback, num_putback);
+
+ size_t n = fread(buffer + putback_size, 1, buffer_size - putback_size, fd);
+ if (n <= 0)
+ return EOF;
+
+ setg(buffer + putback_size - num_putback, buffer + putback_size,
+ buffer + putback_size + n);
+
+ return *gptr();
+}
+
+PStreamInBuf::PStreamInBuf(const std::string & command) :
+ _command(command),
+ fd(popen(command.c_str(), "r"))
+{
+ Log::get_instance()->message(ll_debug, lc_no_context,
+ "popen " + command + " -> " + stringify(fileno(fd)));
+
+ if (0 == fd)
+ throw PStreamError("popen('" + _command + "', 'r') failed: " +
+ std::strerror(errno));
+
+ setg(buffer + putback_size, buffer + putback_size, buffer + putback_size);
+}
+
+PStreamInBuf::~PStreamInBuf()
+{
+ if (0 != fd)
+ {
+ int fdn = fileno(fd), x = pclose(fd);
+ Log::get_instance()->message(ll_debug, lc_no_context,
+ "pclose " + stringify(fdn) + " -> " + stringify(x));
+ }
+}
+
+int
+PStreamInBuf::exit_status()
+{
+ if (0 != fd)
+ {
+ int fdn = fileno(fd);
+ _exit_status = pclose(fd);
+ fd = 0;
+ Log::get_instance()->message(ll_debug, lc_no_context,
+ "manual pclose " + stringify(fdn) + " -> " + stringify(_exit_status));
+ }
+ return _exit_status;
+}
diff --git a/0.4.0/paludis/util/pstream.hh b/0.4.0/paludis/util/pstream.hh
new file mode 100644
index 000000000..af79b8f67
--- /dev/null
+++ b/0.4.0/paludis/util/pstream.hh
@@ -0,0 +1,193 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_PSTREAM_HH
+#define PALUDIS_GUARD_PALUDIS_PSTREAM_HH 1
+
+#include <cstdio>
+#include <istream>
+#include <limits>
+#include <paludis/util/exception.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <streambuf>
+#include <string>
+
+/** \file
+ * Declarations for the PStream and PStreamInBuf classes, and related
+ * utilities.
+ *
+ * \ingroup grpsystem
+ */
+
+namespace paludis
+{
+ /**
+ * Thrown if a PStream or PStreamInBuf encounters an error.
+ *
+ * \ingroup grpsystem
+ * \ingroup grpexceptions
+ */
+ class PStreamError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ PStreamError(const std::string & message) throw ();
+ };
+
+ /**
+ * Input buffer class for a process, invoked using popen(3).
+ *
+ * Bidirectional I/O isn't supported since we haven't needed it yet, and
+ * because popen on Linux is unidirectional.
+ *
+ * See \ref TCppSL Ch. 13.13 for what we're doing here. The buffer code is
+ * based upon the "io/inbuf1.hpp" example in section 13.13.3.
+ *
+ * \ingroup grpsystem
+ */
+ class PStreamInBuf :
+ public std::streambuf,
+ private InstantiationPolicy<PStreamInBuf, instantiation_method::NonCopyableTag>
+ {
+ private:
+ const std::string _command;
+
+ int _exit_status;
+
+ protected:
+ /**
+ * Our file descriptor.
+ */
+ FILE * fd;
+
+ /**
+ * At most how many characters can we put back?
+ */
+ static const int putback_size = std::numeric_limits<unsigned>::digits >> 3;
+
+ /**
+ * How large is our internal buffer?
+ */
+ static const int buffer_size = 3 * putback_size;
+
+ /**
+ * Internal buffer.
+ */
+ char buffer[buffer_size];
+
+ /**
+ * Called when an underflow occurs.
+ */
+ virtual int_type underflow();
+
+ public:
+ /**
+ * Constructor.
+ *
+ * \param command The command to run. See PStream for discussion.
+ */
+ PStreamInBuf(const std::string & command);
+
+ /**
+ * Destructor.
+ */
+ ~PStreamInBuf();
+
+ /**
+ * What was our command?
+ */
+ const std::string & command() const
+ {
+ return _command;
+ }
+
+ /**
+ * What is our exit status?
+ */
+ int exit_status();
+ };
+
+ /**
+ * For internal use by PStream classes.
+ *
+ * \ingroup grpsystem
+ */
+ namespace pstream_internals
+ {
+ /**
+ * Avoid base from member issues for PStream.
+ *
+ * \ingroup grpsystem
+ */
+ struct PStreamInBufBase :
+ private paludis::InstantiationPolicy<PStreamInBufBase, instantiation_method::NonCopyableTag>
+ {
+ /**
+ * Our buffer.
+ */
+ PStreamInBuf buf;
+
+ /**
+ * Constructor.
+ */
+ PStreamInBufBase(const std::string & command) :
+ buf(command)
+ {
+ }
+ };
+ }
+
+ /**
+ * A PStream class is a standard input stream class whose contents comes
+ * from the output of an executed command.
+ *
+ * \ingroup grpsystem
+ */
+ class PStream :
+ private InstantiationPolicy<PStream, instantiation_method::NonCopyableTag>,
+ protected pstream_internals::PStreamInBufBase,
+ public std::istream
+ {
+ public:
+ /**
+ * Constructor.
+ *
+ * \param command The command to execute. PATH is used, so there is
+ * usually no need to specify a full path. Arguments can be passed
+ * as part of the command.
+ */
+ PStream(const std::string & command) :
+ PStreamInBufBase(command),
+ std::istream(&buf)
+ {
+ }
+
+ /**
+ * What is our exit status?
+ */
+ int exit_status()
+ {
+ return buf.exit_status();
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/pstream_TEST.cc b/0.4.0/paludis/util/pstream_TEST.cc
new file mode 100644
index 000000000..0f6566085
--- /dev/null
+++ b/0.4.0/paludis/util/pstream_TEST.cc
@@ -0,0 +1,111 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/pstream.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Tests for PStream.
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test PStream on a normal command.
+ *
+ * \ingroup grptestcases
+ */
+ struct PStreamTest : TestCase
+ {
+ PStreamTest() : TestCase("pstream") { }
+
+ void run()
+ {
+ PStream * p;
+ TEST_CHECK((p = new PStream("echo hi")));
+ std::string line;
+ TEST_CHECK(std::getline(*p, line));
+ TEST_CHECK_EQUAL(line, "hi");
+ TEST_CHECK(! std::getline(*p, line));
+ TEST_CHECK_EQUAL(p->exit_status(), 0);
+ delete p;
+ }
+ } test_pstream;
+
+ /**
+ * \test Test PStream on a command that doesn't exist.
+ *
+ * \ingroup grptestcases
+ */
+ struct PStreamNoExistTest : TestCase
+ {
+ PStreamNoExistTest() : TestCase("pstream nonexistent command") { }
+
+ void run()
+ {
+ PStream p("thiscommanddoesnotexist 2>/dev/null");
+ TEST_CHECK(p.exit_status() != 0);
+ }
+ } test_pstream_no_exist;
+
+ /**
+ * \test Test PStream on a command that returns a failure with no output.
+ *
+ * \ingroup grptestcases
+ */
+ struct PStreamSilentFailTest : TestCase
+ {
+ PStreamSilentFailTest() : TestCase("pstream silent fail") { }
+
+ void run()
+ {
+ PStream p("test -e /doesnotexist");
+ TEST_CHECK(p.exit_status() != 0);
+ }
+ } test_pstream_silent_fail;
+
+ /**
+ * \test Test PStream on a command that fails with output.
+ *
+ * \ingroup grptestcases
+ */
+ struct PStreamFailTest : TestCase
+ {
+ PStreamFailTest() : TestCase("pstream fail") { }
+
+ void run()
+ {
+ PStream * p;
+ TEST_CHECK((p = new PStream("cat /doesnotexist 2>&1")));
+ std::string line;
+ TEST_CHECK(std::getline(*p, line));
+ TEST_CHECK(! line.empty());
+ TEST_CHECK(! std::getline(*p, line));
+ TEST_CHECK(p->exit_status() != 0);
+ delete p;
+ }
+ } test_pstream_fail;
+}
+
diff --git a/0.4.0/paludis/util/random.cc b/0.4.0/paludis/util/random.cc
new file mode 100644
index 000000000..4cb6b85b4
--- /dev/null
+++ b/0.4.0/paludis/util/random.cc
@@ -0,0 +1,43 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "random.hh"
+#include <time.h>
+
+/** \file
+ * Implementation for random.hh.
+ *
+ * \ingroup grprandom
+ */
+
+using namespace paludis;
+
+uint32_t Random::global_seed(0xdeadbeef ^ ::time(0));
+
+Random::Random(uint32_t seed) :
+ local_seed(seed)
+{
+}
+
+Random::Random() :
+ local_seed(global_seed ^ ((::time(0) >> 16) | (::time(0) << 16)))
+{
+ global_seed += local_seed;
+}
+
diff --git a/0.4.0/paludis/util/random.hh b/0.4.0/paludis/util/random.hh
new file mode 100644
index 000000000..6b278c2a1
--- /dev/null
+++ b/0.4.0/paludis/util/random.hh
@@ -0,0 +1,61 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_RANDOM_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_RANDOM_HH 1
+
+#include <cstdlib>
+#include <inttypes.h>
+
+namespace paludis
+{
+ /**
+ * A basic random number generator class, which is not suitable for
+ * cryptography but is fast and reasonably pseudorandom.
+ *
+ * See \ref TCppPL 22.7 for justification. See \ref TaoCP2 3.2.1 for the
+ * basic algorithm and \ref AppCrypt 16.1 for the choice of numbers.
+ *
+ * \ingroup grprandom
+ */
+ class Random
+ {
+ private:
+ static uint32_t global_seed;
+ uint32_t local_seed;
+
+ static const uint32_t _a = 2416;
+ static const uint32_t _b = 374441;
+ static const uint32_t _m = 1771875;
+
+ public:
+ Random(uint32_t seed);
+ Random();
+
+ template <typename DiffType_>
+ DiffType_ operator() (DiffType_ max)
+ {
+ local_seed = (_a * local_seed + _b) % _m;
+ double t(static_cast<double>(local_seed) / static_cast<double>(_m));
+ return static_cast<DiffType_>(t * max);
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/random_TEST.cc b/0.4.0/paludis/util/random_TEST.cc
new file mode 100644
index 000000000..ef4489d46
--- /dev/null
+++ b/0.4.0/paludis/util/random_TEST.cc
@@ -0,0 +1,121 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "random.hh"
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+#include <algorithm>
+
+/** \file
+ * Test cases for paludis::Random.
+ *
+ * \ingroup grptestcases
+ */
+
+using namespace paludis;
+using namespace test;
+
+namespace
+{
+ struct RandomUpTo
+ {
+ Random random;
+ unsigned max;
+
+ RandomUpTo(const Random & r, const unsigned m) :
+ random(r),
+ max(m)
+ {
+ }
+
+ int operator() ()
+ {
+ return random(max);
+ }
+ };
+
+ inline double square(double v)
+ {
+ return v * v;
+ }
+}
+
+namespace test_cases
+{
+ /**
+ * \test Test Random distibutions using counts.
+ *
+ * \ingroup grptestcases
+ */
+ struct RandomDistributionCountsTest : TestCase
+ {
+ RandomDistributionCountsTest() : TestCase("distribution (counts)") { }
+
+ void run()
+ {
+ std::vector<int> v;
+ v.reserve(10000);
+ std::generate_n(std::back_inserter(v), 10000, RandomUpTo(Random(), 10));
+
+ TEST_CHECK_EQUAL(0, *std::min_element(v.begin(), v.end()));
+ TEST_CHECK_EQUAL(9, *std::max_element(v.begin(), v.end()));
+ for (int i(0) ; i < 10 ; ++i)
+ {
+ TEST_CHECK(std::count(v.begin(), v.end(), i) > 1);
+ TEST_CHECK(std::count(v.begin(), v.end(), i) < 3000);
+ }
+ }
+ } test_random_counts;
+
+ /**
+ * \test Test Random distibutions using chi square.
+ *
+ * This is a chi square test, so it could theoretically fail
+ * occasionally. See \ref TaoCP2 3.3.1 for details.
+ */
+ struct RandomDistributionChiSquaredTest : TestCase
+ {
+ RandomDistributionChiSquaredTest() : TestCase("distribution (chi square)") { }
+
+ void run()
+ {
+ int failures(0);
+
+ for (int attempts(0) ; attempts < 3 ; ++ attempts)
+ {
+ std::vector<int> v;
+ v.reserve(10000);
+ std::generate_n(std::back_inserter(v), 10000, RandomUpTo(Random(), 10));
+
+ double a(0);
+ for (int i(0) ; i <= 9 ; ++i)
+ a += (square(std::count(v.begin(), v.end(), i) - (10000 / 10)) / (10000 / 10));
+
+ TestMessageSuffix suffix("a=" + stringify(a), true);
+ TEST_CHECK(true);
+ if ((a < 2.088) || (a > 21.67))
+ ++failures;
+ }
+
+ TEST_CHECK(failures <= 1);
+ }
+ } test_random_chi_square;
+}
+
diff --git a/0.4.0/paludis/util/save.hh b/0.4.0/paludis/util/save.hh
new file mode 100644
index 000000000..cbc55eafd
--- /dev/null
+++ b/0.4.0/paludis/util/save.hh
@@ -0,0 +1,78 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_SAVE_HH
+#define PALUDIS_GUARD_PALUDIS_SAVE_HH 1
+
+#include <paludis/util/instantiation_policy.hh>
+
+/** \file
+ * Declarations for the Save class.
+ *
+ * \ingroup grpsave
+ */
+
+namespace paludis
+{
+ /**
+ * Save the value of a particular variable and assign it a new value for the
+ * duration of the Save instance's lifetime (RAII, see \ref EffCpp item 13 or
+ * \ref TCppPL section 14.4).
+ *
+ * \ingroup grpsave
+ */
+ template <typename T_>
+ class Save :
+ private InstantiationPolicy<Save<T_>, instantiation_method::NonCopyableTag>
+ {
+ private:
+ T_ * const _ptr;
+ const T_ _value;
+
+ public:
+ /**
+ * Constructor.
+ */
+ Save(T_ * const p) :
+ _ptr(p),
+ _value(*p)
+ {
+ }
+
+ /**
+ * Constructor, with convenience assignment to new_value.
+ */
+ Save(T_ * const p, const T_ & new_value) :
+ _ptr(p),
+ _value(*p)
+ {
+ *p = new_value;
+ }
+
+ /**
+ * Destructor.
+ */
+ ~Save()
+ {
+ *_ptr = _value;
+ }
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/save_TEST.cc b/0.4.0/paludis/util/save_TEST.cc
new file mode 100644
index 000000000..8f3313651
--- /dev/null
+++ b/0.4.0/paludis/util/save_TEST.cc
@@ -0,0 +1,68 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/save.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for save.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test Save.
+ *
+ * \ingroup grptestcases
+ */
+ struct SaveTest : TestCase
+ {
+ SaveTest() : TestCase("save") { }
+
+ void run()
+ {
+ std::string s("one");
+ TEST_CHECK_EQUAL(s, "one");
+ {
+ Save<std::string> save_s(&s);
+ TEST_CHECK_EQUAL(s, "one");
+ s = "two";
+ TEST_CHECK_EQUAL(s, "two");
+ }
+ TEST_CHECK_EQUAL(s, "one");
+ {
+ Save<std::string> save_s(&s, "three");
+ TEST_CHECK_EQUAL(s, "three");
+ {
+ Save<std::string> save_s_2(&s, "four");
+ TEST_CHECK_EQUAL(s, "four");
+ }
+ TEST_CHECK_EQUAL(s, "three");
+ }
+ TEST_CHECK_EQUAL(s, "one");
+ }
+ } test_save;
+}
+
diff --git a/0.4.0/paludis/util/smart_record.hh.m4 b/0.4.0/paludis/util/smart_record.hh.m4
new file mode 100644
index 000000000..7718f44ad
--- /dev/null
+++ b/0.4.0/paludis/util/smart_record.hh.m4
@@ -0,0 +1,924 @@
+#if 0
+ifdef(`__gnu__',`',`errprint(`This is not GNU m4...
+')m4exit(1)') include(`misc/generated-file.txt')
+dnl vim: set ft=cpp et sw=4 sts=4 :
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+define(`max_record_size', `15')
+define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
+define(`_forloop', `$4`'ifelse($1, `$3', , `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
+#endif
+
+#ifndef PALUDIS_GUARD_PALUDIS_SMART_RECORD_HH
+#define PALUDIS_GUARD_PALUDIS_SMART_RECORD_HH 1
+
+#include <paludis/util/comparison_policy.hh>
+#include <paludis/util/compare.hh>
+#include <paludis/util/exception.hh>
+#include <string>
+
+/** \file
+ * SmartRecord declarations.
+ *
+ * \ingroup grprecords
+ */
+
+namespace paludis
+{
+ namespace comparison_method
+ {
+ /**
+ * Comparisons are done by considering each member in order.
+ *
+ * \ingroup grprecords
+ */
+ struct SmartRecordCompareByAllTag
+ {
+ };
+
+ /**
+ * Comparisons are done by considering a specific key.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned key_>
+ struct SmartRecordCompareByKeyTag
+ {
+ };
+
+ /**
+ * Comparisons are done by considering a range of keys.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned first_key_, unsigned last_key_>
+ struct SmartRecordCompareByKeyRangeTag
+ {
+ };
+ }
+
+ /**
+ * Provides the basic typedefs required for a MakeSmartRecord instantiation.
+ *
+ * \ingroup grprecords
+ */
+ template <
+ typename ComparisonModeTag_ = comparison_mode::NoComparisonTag,
+ typename ComparisonMethod_ = comparison_method::SmartRecordCompareByAllTag>
+ struct SmartRecordTag
+ {
+ /**
+ * Mode used for ComparisonPolicy.
+ *
+ * \ingroup grprecords
+ */
+ typedef ComparisonModeTag_ ComparisonModeTag;
+
+ /**
+ * Method used for ComparisonPolicy, should be either
+ * comparison_method::SmartRecordCompareByAllTag or
+ * comparison_method::SmartRecordCompareByKeyTag .
+ *
+ * \ingroup grprecords
+ */
+ typedef ComparisonMethod_ ComparisonMethodTag;
+ };
+
+ /**
+ * Provides the key information typedefs for a MakeSmartRecord
+ * instantiation.
+ *
+ * \ingroup grprecords
+ */
+ template <typename E_, unsigned Count_>
+ struct SmartRecordKeys
+ {
+ /**
+ * The type of our keys.
+ */
+ typedef E_ Keys;
+
+ /**
+ * The number of keys.
+ */
+ static const unsigned key_count = Count_;
+ };
+
+ template <unsigned Idx_, typename T_>
+ struct SmartRecordKey;
+
+forloop(`idx', `0', max_record_size, `
+ /**
+ * Provides the key information typedefs for a MakeSmartRecord
+ * instantiation.
+ *
+ * \ingroup grprecords
+ */
+ template <typename T_>
+ struct SmartRecordKey<`'idx`', T_>
+ {
+ typedef T_ KeyType`'idx`';
+ };
+')
+
+ /**
+ * Internal use by SmartRecord.
+ *
+ * \ingroup grprecords
+ */
+ namespace smart_record_internals
+ {
+ /**
+ * Internal use by SmartRecord: turn a string literal into a string.
+ *
+ * \ingroup grprecords
+ */
+ template <typename T_>
+ struct CharStarToString
+ {
+ /// Our type, unconverted.
+ typedef T_ Type;
+ };
+
+ /**
+ * Internal use by SmartRecord: turn a string literal into a string.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned i_>
+ struct CharStarToString<char [i_]>
+ {
+ /// We are a string.
+ typedef std::string Type;
+ };
+
+ /**
+ * Internal use by SmartRecord: turn a string literal into a string.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned i_>
+ struct CharStarToString<const char [i_]>
+ {
+ /// We are a string.
+ typedef std::string Type;
+ };
+
+ /**
+ * Internal use by SmartRecord: turn a string literal into a string.
+ *
+ * \ingroup grprecords
+ */
+ template <>
+ struct CharStarToString<char *>
+ {
+ /// We are a string.
+ typedef std::string Type;
+ };
+
+ /**
+ * Internal use by SmartRecord: tail of a list.
+ *
+ * \ingroup grprecords
+ */
+ struct ParamListTail
+ {
+ /// We have no children.
+ static const unsigned list_length = 0;
+ };
+
+ /**
+ * Internal use by SmartRecord: a list of parameters.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned left_idx_, typename L_, typename R_>
+ struct ParamList
+ {
+ /// Our left index.
+ static const unsigned left_idx = left_idx_;
+
+ /// Our left item.
+ L_ left;
+
+ /// Type of our left item.
+ typedef L_ LeftType;
+
+ /// Our right item.
+ R_ right;
+
+ /// Type of our right item.
+ typedef R_ RightType;
+
+ /// How long are we?
+ static const unsigned list_length = 1 + RightType::list_length;
+
+ /// Constructor.
+ ParamList(const L_ & l, const R_ & r) :
+ left(l),
+ right(r)
+ {
+ }
+ };
+
+ template <unsigned idx_, typename Result_, unsigned left_idx_, typename L_, typename R_>
+ Result_
+ find_list_entry(const ParamList<left_idx_, L_, R_> & list);
+
+ /**
+ * Find a list entry.
+ *
+ * \ingroup grprecords
+ */
+ template <bool is_left, unsigned idx_, typename Result_, unsigned left_idx_, typename L_, typename R_>
+ struct FindListEntry;
+
+ /**
+ * Find a list entry.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned idx_, typename Result_, unsigned left_idx_, typename L_, typename R_>
+ struct FindListEntry<true, idx_, Result_, left_idx_, L_, R_>
+ {
+ Result_ operator() (const ParamList<left_idx_, L_, R_> & list) const
+ {
+ return list.left;
+ }
+ };
+
+ /**
+ * Find a list entry.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned idx_, typename Result_, unsigned left_idx_, typename L_, typename R_>
+ struct FindListEntry<false, idx_, Result_, left_idx_, L_, R_>
+ {
+ Result_ operator() (const ParamList<left_idx_, L_, R_> & list) const
+ {
+ return find_list_entry<idx_, Result_>(list.right);
+ }
+ };
+
+ /**
+ * Find a list entry.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned idx_, typename Result_, unsigned left_idx_, typename L_, typename R_>
+ Result_
+ find_list_entry(const ParamList<left_idx_, L_, R_> & list)
+ {
+ return FindListEntry<left_idx_ == idx_, idx_, Result_, left_idx_, L_, R_>()(list);
+ }
+
+ /**
+ * A node in a param list.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned idx_, typename T_>
+ struct ParamListNode
+ {
+ /// Our item.
+ T_ value;
+
+ /// Our index.
+ static const unsigned idx = idx_;
+
+ /// Constructor.
+ ParamListNode(const T_ & t) :
+ value(t)
+ {
+ }
+ };
+
+ /**
+ * Join param nodes.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned left_idx_, typename T_, unsigned right_idx_, typename U_>
+ ParamList<left_idx_, T_, ParamList<right_idx_, U_, ParamListTail> >
+ operator, (const ParamListNode<left_idx_, T_> & t, const ParamListNode<right_idx_, U_> & u)
+ {
+ return ParamList<left_idx_, T_, ParamList<right_idx_, U_, ParamListTail> >(t.value,
+ ParamList<right_idx_, U_, ParamListTail>(u.value, ParamListTail()));
+ }
+
+ /**
+ * Join param nodes.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned left_idx_, typename T_, typename U_, unsigned right_idx_, typename V_>
+ ParamList<left_idx_, V_, ParamList<right_idx_, T_, U_> >
+ operator, (const ParamList<right_idx_, T_, U_> & t, const ParamListNode<left_idx_, V_> & u)
+ {
+ return ParamList<left_idx_, V_, ParamList<right_idx_, T_, U_> >(u.value, t);
+ }
+
+ template <typename Tag_, unsigned Key_>
+ struct GetRecordKeyType;
+
+forloop(`idx', `0', max_record_size, `
+ /**
+ * Get the type of a particular key in a record.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_>
+ struct GetRecordKeyType<Tag_, `'idx`'>
+ {
+ typedef typename Tag_::KeyType`'idx`' Type;
+ };
+')
+
+ template <typename Tag_, unsigned key_count_>
+ struct RecordBase;
+
+ template <typename Tag_, unsigned key_count_, unsigned Key_>
+ struct RecordKeyGetter;
+
+ template <typename Tag_, unsigned key_count_, unsigned Key_>
+ struct RecordKeyPointerGetter;
+
+forloop(`idx', `0', max_record_size, `
+ /**
+ * Handle fetching a record key.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_>
+ struct RecordKeyGetter<Tag_, key_count_, `'idx`'>
+ {
+ /**
+ * Fetch, const.
+ */
+ static const typename GetRecordKeyType<Tag_, `'idx`'>::Type &
+ do_get(const RecordBase<Tag_, key_count_> & r)
+ {
+ return r._v`'idx`';
+ }
+
+ /**
+ * Fetch, non const.
+ */
+ static typename GetRecordKeyType<Tag_, `'idx`'>::Type &
+ do_get(RecordBase<Tag_, key_count_> & r)
+ {
+ return r._v`'idx`';
+ }
+
+ /**
+ * Set.
+ */
+ static void
+ do_set(RecordBase<Tag_, key_count_> & r,
+ const typename GetRecordKeyType<Tag_, `'idx`'>::Type & v)
+ {
+ r._v`'idx`' = v;
+ }
+ };
+')
+
+ template <typename Tag_, unsigned key_count_, typename ComparisonModeTag_, typename ComparisonMethodTag_>
+ struct RecordComparisonBase;
+
+ /**
+ * RecordComparisonBase: specialisation for comparison_mode::NoComparisonTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, typename ComparisonMethodTag_>
+ struct RecordComparisonBase<Tag_, key_count_, comparison_mode::NoComparisonTag, ComparisonMethodTag_> :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::NoComparisonTag, ComparisonMethodTag_>
+ {
+ };
+
+ /**
+ * RecordComparisonBase: specialisation for
+ * comparison_mode::EqualityComparisonTag and
+ * comparison_method::SmartRecordCompareByKeyTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, unsigned key_>
+ class RecordComparisonBase<Tag_, key_count_, comparison_mode::EqualityComparisonTag,
+ comparison_method::SmartRecordCompareByKeyTag<key_> > : public
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<typename GetRecordKeyType<Tag_, key_>::Type> >
+ {
+ public:
+ /// Constructor.
+ RecordComparisonBase();
+
+ /// Copy constructor.
+ RecordComparisonBase(const RecordComparisonBase & other);
+ };
+
+ /**
+ * RecordComparisonBase: specialisation for
+ * comparison_mode::FullComparisonTag and
+ * comparison_method::SmartRecordCompareByKeyTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, unsigned key_>
+ class RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByKeyTag<key_> > : public
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<typename GetRecordKeyType<Tag_, key_>::Type> >
+ {
+ public:
+ /// Constructor.
+ RecordComparisonBase();
+
+ /// Copy constructor.
+ RecordComparisonBase(const RecordComparisonBase & other);
+ };
+
+ /**
+ * RecordComparisonBase: specialisation for
+ * comparison_mode::EqualityComparisonTag and
+ * comparison_method::SmartRecordCompareByAllTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_>
+ class RecordComparisonBase<Tag_, key_count_, comparison_mode::EqualityComparisonTag,
+ comparison_method::SmartRecordCompareByAllTag> : public
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>
+ {
+ protected:
+ /// Do the comparison.
+ bool compare(const RecordBase<Tag_, key_count_> & other) const;
+
+ public:
+ /// Constructor.
+ RecordComparisonBase() :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(&RecordComparisonBase::compare)
+ {
+ }
+
+ /// Copy constructor.
+ RecordComparisonBase(const RecordComparisonBase & other) :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(other)
+ {
+ }
+ };
+
+ /**
+ * RecordComparisonBase: specialisation for
+ * comparison_mode::FullComparisonTag and
+ * comparison_method::SmartRecordCompareByAllTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_>
+ class RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByAllTag> : public
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>
+ {
+ protected:
+ /// Do the comparison.
+ int compare(const RecordBase<Tag_, key_count_> & other) const;
+
+ public:
+ /// Constructor.
+ RecordComparisonBase() :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(&RecordComparisonBase::compare)
+ {
+ }
+
+ /// Copy constructor.
+ RecordComparisonBase(const RecordComparisonBase & other) :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(other)
+ {
+ }
+ };
+
+ /**
+ * RecordComparisonBase: specialisation for
+ * comparison_mode::EqualityComparisonTag and
+ * comparison_method::SmartRecordCompareByKeyRangeTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, unsigned first_key_, unsigned last_key_>
+ class RecordComparisonBase<Tag_, key_count_, comparison_mode::EqualityComparisonTag,
+ comparison_method::SmartRecordCompareByKeyRangeTag<first_key_, last_key_> > : public
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>
+ {
+ protected:
+ /// Do the comparison.
+ bool compare(const RecordBase<Tag_, key_count_> & other) const;
+
+ public:
+ /// Constructor.
+ RecordComparisonBase() :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(&RecordComparisonBase::compare)
+ {
+ }
+
+ /// Copy constructor.
+ RecordComparisonBase(const RecordComparisonBase & other) :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(other)
+ {
+ }
+ };
+
+ /**
+ * RecordComparisonBase: specialisation for
+ * comparison_mode::FullComparisonTag and
+ * comparison_method::SmartRecordCompareByKeyRangeTag.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, unsigned first_key_, unsigned last_key_>
+ class RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByKeyRangeTag<first_key_, last_key_> > : public
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>
+ {
+ protected:
+ /// Do the comparison.
+ int compare(const RecordBase<Tag_, key_count_> & other) const;
+
+ public:
+ /// Constructor.
+ RecordComparisonBase() :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(&RecordComparisonBase::compare)
+ {
+ }
+
+ /// Copy constructor.
+ RecordComparisonBase(const RecordComparisonBase & other) :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberComparisonFunctionTag>(other)
+ {
+ }
+ };
+
+forloop(`idx', `1', max_record_size, `
+ /**
+ * Base class for a SmartRecord.
+ *
+ * \ingroup grprecords
+ */
+ template<typename Tag_>
+ class RecordBase<Tag_, `'idx`'> : public RecordComparisonBase<Tag_, `'idx`',
+ typename Tag_::ComparisonModeTag,
+ typename Tag_::ComparisonMethodTag>
+ {
+forloop(`idy', `0', decr(`'idx`'), `
+ friend class RecordKeyGetter<Tag_, `'idx`', `'idy`'>;
+ friend class RecordKeyPointerGetter<Tag_, `'idx`', `'idy`'>;
+')
+
+ private:
+forloop(`idy', `0', decr(`'idx`'), `
+ typename Tag_::KeyType`'idy`' _v`'idy`';
+')
+
+ public:
+ /// Destructor.
+ ~RecordBase();
+
+ /// Constructor, from raw parameters.
+ RecordBase(
+ifelse(idx, `1', `', `forloop(`idy', `0', decr(decr(idx)), `
+ const typename Tag_::KeyType`'idy`' & p`'idy`',
+') ')
+ const typename Tag_::KeyType`'decr(idx)`' & p`'decr(idx)`'
+ ) :
+ifelse(idx, `1', `', `forloop(`idy', `0', decr(decr(idx)), `
+ _v`'idy`'(p`'idy`'),
+') ')
+ _v`'decr(idx)`'(p`'decr(idx)`')
+ {
+ }
+
+ /// Copy constructor.
+ RecordBase(const RecordBase<Tag_, `'idx`'> & other) :
+ RecordComparisonBase<Tag_, `'idx`', typename Tag_::ComparisonModeTag,
+ typename Tag_::ComparisonMethodTag>(other),
+ifelse(idx, `1', `', `forloop(`idy', `0', decr(decr(idx)), `
+ _v`'idy`'(other._v`'idy`'),
+') ')
+ _v`'decr(idx)`'(other._v`'decr(idx)`')
+ {
+ }
+
+ /// Assignment.
+ const RecordBase & operator= (const RecordBase<Tag_, `'idx`'> & other)
+ {
+forloop(`idy', `0', decr(idx), `
+ _v`'idy`' = other._v`'idy`';
+')
+ return *this;
+ }
+
+ /// Fetch a key type.
+ template <typename Tag_::Keys k_>
+ struct GetKeyType
+ {
+ /// The key type.
+ typedef typename GetRecordKeyType<Tag_, k_>::Type Type;
+ };
+
+ /// Fetch an item.
+ template <typename Tag_::Keys k_>
+ const typename GetRecordKeyType<Tag_, k_>::Type & get() const
+ {
+ return RecordKeyGetter<Tag_, `'idx`', k_>::do_get(*this);
+ }
+
+ /// Fetch an item.
+ template <typename Tag_::Keys k_>
+ typename GetRecordKeyType<Tag_, k_>::Type & get()
+ {
+ return RecordKeyGetter<Tag_, `'idx`', k_>::do_get(*this);
+ }
+
+ /// Set an item.
+ template <typename Tag_::Keys k_>
+ void set(const typename GetRecordKeyType<Tag_, k_>::Type & v)
+ {
+ return RecordKeyGetter<Tag_, `'idx`', k_>::do_set(*this, v);
+ }
+
+ /// Named parameters constructor.
+ template <typename List_>
+ static RecordBase
+ create(const List_ & list)
+ {
+ return RecordBase(
+ifelse(idx, `1', `', `forloop(`idy', `0', decr(decr(idx)), `
+ find_list_entry<`'idy`', typename Tag_::KeyType`'idy`'>(list),
+') ')
+ find_list_entry<`'decr(idx)`', typename Tag_::KeyType`'decr(idx)`'>(list)
+ );
+ }
+ };
+
+ template<typename Tag_>
+ RecordBase<Tag_, `'idx`'>::~RecordBase()
+ {
+ }
+')
+
+forloop(`idx', `0', max_record_size, `
+ /**
+ * Fetch a pointer to a record key.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_>
+ struct RecordKeyPointerGetter<Tag_, key_count_, `'idx`'>
+ {
+ /**
+ * Fetch the pointer.
+ */
+ static typename GetRecordKeyType<Tag_, `'idx>::Type
+ RecordBase<Tag_, key_count_>::* do_get_pointer()
+ {
+ return &RecordBase<Tag_, key_count_>::_v`'idx`';
+ }
+ };
+')
+
+ template <typename Tag_, unsigned key_count_, unsigned key_>
+ RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByKeyTag<key_> >::RecordComparisonBase() :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<typename GetRecordKeyType<Tag_, key_>::Type> >(
+ RecordKeyPointerGetter<Tag_, key_count_, key_>::do_get_pointer())
+ {
+ }
+
+ template <typename Tag_, unsigned key_count_, unsigned key_>
+ RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByKeyTag<key_> >::RecordComparisonBase(
+ const RecordComparisonBase & other) :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<typename GetRecordKeyType<Tag_, key_>::Type> >(other)
+ {
+ }
+
+ template <typename Tag_, unsigned key_count_, unsigned key_>
+ RecordComparisonBase<Tag_, key_count_, comparison_mode::EqualityComparisonTag,
+ comparison_method::SmartRecordCompareByKeyTag<key_> >::RecordComparisonBase() :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<typename GetRecordKeyType<Tag_, key_>::Type> >(
+ RecordKeyPointerGetter<Tag_, key_count_, key_>::do_get_pointer())
+ {
+ }
+
+ template <typename Tag_, unsigned key_count_, unsigned key_>
+ RecordComparisonBase<Tag_, key_count_, comparison_mode::EqualityComparisonTag,
+ comparison_method::SmartRecordCompareByKeyTag<key_> >::RecordComparisonBase(
+ const RecordComparisonBase & other) :
+ ComparisonPolicy<RecordBase<Tag_, key_count_>, comparison_mode::EqualityComparisonTag,
+ comparison_method::CompareByMemberTag<typename GetRecordKeyType<Tag_, key_>::Type> >(other)
+ {
+ }
+
+ template <typename Tag_, unsigned key_count_>
+ struct DoFullCompareByAll;
+
+forloop(`idx', `1', max_record_size, `
+ /**
+ * Mixin class for SmartRecord instances that are compared by all
+ * keys in order.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_>
+ struct DoFullCompareByAll<Tag_, `'idx`'>
+ {
+ /// Do the comparison.
+ static int do_compare(const RecordBase<Tag_, `'idx`'> * const a,
+ const RecordBase<Tag_, `'idx`'> * const b)
+ {
+forloop(`idy', `0', decr(`'idx`'), `
+ switch (compare(
+ RecordKeyGetter<Tag_, `'idx`', `'idy`'>::do_get(*a),
+ RecordKeyGetter<Tag_, `'idx`', `'idy`'>::do_get(*b)))
+ {
+ case -1:
+ return -1;
+ case 1:
+ return 1;
+ case 0:
+ break;
+ default:
+ throw InternalError(PALUDIS_HERE, "Bad value from compare");
+ }
+')
+ return 0;
+ }
+ };
+')
+
+ template <typename Tag_, unsigned key_count_>
+ struct DoEqualCompareByAll;
+
+forloop(`idx', `1', max_record_size, `
+ /**
+ * Mixin class for SmartRecord instances that are equal-compared by all
+ * keys in order.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_>
+ struct DoEqualCompareByAll<Tag_, `'idx`'>
+ {
+ static bool do_compare(const RecordBase<Tag_, `'idx`'> * const a,
+ const RecordBase<Tag_, `'idx`'> * const b)
+ {
+forloop(`idy', `0', decr(`'idx`'), `
+ if (RecordKeyGetter<Tag_, `'idx`', `'idy`'>::do_get(*a) !=
+ RecordKeyGetter<Tag_, `'idx`', `'idy`'>::do_get(*b))
+ return false;
+')
+ return true;
+ }
+ };
+')
+
+ template <typename Tag_, unsigned key_count_>
+ int RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByAllTag>::compare(
+ const RecordBase<Tag_, key_count_> & other) const
+ {
+ return DoFullCompareByAll<Tag_, key_count_>::do_compare(
+ static_cast<const RecordBase<Tag_, key_count_> *>(this), &other);
+ }
+
+ template <typename Tag_, unsigned key_count_>
+ bool RecordComparisonBase<Tag_, key_count_, comparison_mode::EqualityComparisonTag,
+ comparison_method::SmartRecordCompareByAllTag>::compare(
+ const RecordBase<Tag_, key_count_> & other) const
+ {
+ return DoEqualCompareByAll<Tag_, key_count_>::do_compare(
+ static_cast<const RecordBase<Tag_, key_count_> *>(this), &other);
+ }
+
+ /**
+ * Do the comparison for a range of keys.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, unsigned from_key_, unsigned count_>
+ struct DoFullCompareByKeyRange
+ {
+ /**
+ * Do the comparison.
+ *
+ * \ingroup grprecords
+ */
+ static int do_compare(const RecordBase<Tag_, key_count_> * const a,
+ const RecordBase<Tag_, key_count_> * const b)
+ {
+ if (RecordKeyGetter<Tag_, key_count_, from_key_>::do_get(*a) <
+ RecordKeyGetter<Tag_, key_count_, from_key_>::do_get(*b))
+ return -1;
+ if (RecordKeyGetter<Tag_, key_count_, from_key_>::do_get(*a) >
+ RecordKeyGetter<Tag_, key_count_, from_key_>::do_get(*b))
+ return 1;
+
+ return DoFullCompareByKeyRange<Tag_, key_count_, from_key_ + 1, count_ - 1>::do_compare(a, b);
+ }
+ };
+
+ /**
+ * Do the comparison for a range of keys (specialisation for zero sized
+ * ranges).
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_, unsigned key_count_, unsigned from_key_>
+ struct DoFullCompareByKeyRange<Tag_, key_count_, from_key_, 0>
+ {
+ /**
+ * Do the comparison.
+ */
+ static int do_compare(const RecordBase<Tag_, key_count_> * const,
+ const RecordBase<Tag_, key_count_> * const)
+ {
+ return 0;
+ }
+ };
+
+#ifndef DOXYGEN
+ template <typename Tag_, unsigned key_count_, unsigned from_key_, unsigned to_key_>
+ int RecordComparisonBase<Tag_, key_count_, comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByKeyRangeTag<from_key_, to_key_> >::compare(
+ const RecordBase<Tag_, key_count_> & other) const
+ {
+ return DoFullCompareByKeyRange<Tag_, key_count_, from_key_, to_key_ - from_key_>::do_compare(
+ static_cast<const RecordBase<Tag_, key_count_> *>(this), &other);
+ }
+#endif
+
+ }
+
+ /**
+ * Create a SmartRecord with the attributes described by Tag_.
+ *
+ * \ingroup grprecords
+ */
+ template <typename Tag_>
+ struct MakeSmartRecord
+ {
+ /**
+ * The type of our SmartRecord.
+ *
+ * \ingroup grprecords
+ */
+ typedef smart_record_internals::RecordBase<Tag_, Tag_::key_count> Type;
+ };
+
+ /**
+ * Create a named parameter for creating a SmartRecord instance.
+ *
+ * \ingroup grprecords
+ */
+ template <unsigned idx_, typename T_>
+ smart_record_internals::ParamListNode<idx_, typename smart_record_internals::CharStarToString<T_>::Type>
+ param(const T_ & t)
+ {
+ return smart_record_internals::ParamListNode<idx_,
+ typename smart_record_internals::CharStarToString<T_>::Type>(t);
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/smart_record_TEST.cc b/0.4.0/paludis/util/smart_record_TEST.cc
new file mode 100644
index 000000000..e24e342fe
--- /dev/null
+++ b/0.4.0/paludis/util/smart_record_TEST.cc
@@ -0,0 +1,200 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/smart_record.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+#include <iterator>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Test cases for smart_record.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace
+{
+ enum PersonKeys
+ {
+ firstname,
+ surname,
+ age
+ };
+
+ struct PersonRecordTag :
+ SmartRecordTag<
+ comparison_mode::FullComparisonTag,
+ comparison_method::SmartRecordCompareByAllTag
+ >,
+ SmartRecordKeys<PersonKeys, 3>,
+ SmartRecordKey<firstname, std::string>,
+ SmartRecordKey<surname, std::string>,
+ SmartRecordKey<age, unsigned>
+ {
+ };
+
+ typedef MakeSmartRecord<PersonRecordTag>::Type Person;
+
+ enum PairKeys
+ {
+ first,
+ second
+ };
+
+ struct PairTag :
+ SmartRecordTag<comparison_mode::FullComparisonTag, comparison_method::SmartRecordCompareByAllTag>,
+ SmartRecordKeys<PairKeys, 2>,
+ SmartRecordKey<first, int>,
+ SmartRecordKey<second, int>
+ {
+ };
+
+ typedef MakeSmartRecord<PairTag>::Type Pair;
+}
+
+namespace test_cases
+{
+ /**
+ * \test Test a simple SmartRecord.
+ *
+ * \ingroup grptestcases
+ */
+ struct SimpleRecordTest : TestCase
+ {
+ SimpleRecordTest() : TestCase("simple") { }
+
+ void run()
+ {
+ const Person p1("blah", "first", 10);
+ TEST_CHECK_EQUAL(p1.get<firstname>(), "blah");
+ TEST_CHECK_EQUAL(p1.get<surname>(), "first");
+ TEST_CHECK_EQUAL(p1.get<age>(), 10);
+
+ const Person p2("blah", "second", 6);
+ TEST_CHECK_EQUAL(p2.get<firstname>(), "blah");
+ TEST_CHECK_EQUAL(p2.get<surname>(), "second");
+ TEST_CHECK_EQUAL(p2.get<age>(), 6);
+
+ TEST_CHECK( (p1 < p2));
+ TEST_CHECK( (p1 <= p2));
+ TEST_CHECK(!(p1 == p2));
+ TEST_CHECK( (p1 != p2));
+ TEST_CHECK(!(p1 >= p2));
+ TEST_CHECK(!(p1 > p2));
+
+ Person p3("foo", "bar", 1);
+ TEST_CHECK_EQUAL(p3.get<age>(), 1);
+ p3.set<age>(2);
+ TEST_CHECK_EQUAL(p3.get<age>(), 2);
+ p3.get<age>() = 4;
+ TEST_CHECK_EQUAL(p3.get<age>(), 4);
+ }
+ } test_simple_record;
+
+ /**
+ * \test Test a list constructed SmartRecord.
+ *
+ * \ingroup grptestcases
+ */
+ struct ListConstructedRecordTest : TestCase
+ {
+ ListConstructedRecordTest() : TestCase("list constructed") { }
+
+ void run()
+ {
+ const Person p1(Person::create((
+ param<firstname>("first"),
+ param<surname>("sur"),
+ param<age>(10))));
+
+ TEST_CHECK_EQUAL(p1.get<firstname>(), "first");
+ TEST_CHECK_EQUAL(p1.get<surname>(), "sur");
+ TEST_CHECK_EQUAL(p1.get<age>(), 10);
+
+ const Person p2(Person::create((
+ param<surname>("bar"),
+ param<firstname>("foo"),
+ param<age>(42))));
+
+ TEST_CHECK_EQUAL(p2.get<firstname>(), "foo");
+ TEST_CHECK_EQUAL(p2.get<surname>(), "bar");
+ TEST_CHECK_EQUAL(p2.get<age>(), 42);
+ }
+ } test_list_constructed_record;
+
+ /**
+ * \test Test a SmartRecord of pairs of items.
+ *
+ * \ingroup grptestcases
+ */
+ struct PairTest : TestCase
+ {
+ PairTest() : TestCase("pair") { }
+
+ void run()
+ {
+ std::vector<Pair> v;
+ v.push_back(Pair(0, 0));
+ v.push_back(Pair(0, 1));
+ v.push_back(Pair(0, 2));
+ v.push_back(Pair(1, 0));
+ v.push_back(Pair(1, 1));
+ v.push_back(Pair(1, 2));
+ v.push_back(Pair(2, 0));
+
+ std::vector<Pair>::iterator v1(v.begin()), v_end(v.end());
+ for ( ; v1 != v_end ; ++v1)
+ {
+ TestMessageSuffix s1("v1:" + stringify(v1->get<first>()) + "/"
+ + stringify(v1->get<second>()), true);
+ std::vector<Pair>::iterator v2(v.begin());
+ for ( ; v2 != v_end ; ++v2)
+ {
+ TestMessageSuffix s2("v2:" + stringify(v2->get<first>()) + "/"
+ + stringify(v2->get<second>()), true);
+
+ if (std::distance(v.begin(), v1) < std::distance(v.begin(), v2))
+ {
+ TEST_CHECK(*v1 < *v2);
+ TEST_CHECK(*v2 > *v1);
+ TEST_CHECK(*v1 != *v2);
+ TEST_CHECK(*v2 != *v1);
+ }
+ else if (std::distance(v.begin(), v1) > std::distance(v.begin(), v2))
+ {
+ TEST_CHECK(*v2 < *v1);
+ TEST_CHECK(*v1 > *v2);
+ TEST_CHECK(*v2 != *v1);
+ TEST_CHECK(*v1 != *v2);
+ }
+ else
+ {
+ TEST_CHECK(*v2 == *v1);
+ TEST_CHECK(*v1 == *v2);
+ }
+ }
+ }
+ }
+ } test_pair;
+}
+
diff --git a/0.4.0/paludis/util/stringify.hh b/0.4.0/paludis/util/stringify.hh
new file mode 100644
index 000000000..95ade487b
--- /dev/null
+++ b/0.4.0/paludis/util/stringify.hh
@@ -0,0 +1,168 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_STRINGIFY_HH
+#define PALUDIS_GUARD_PALUDIS_STRINGIFY_HH 1
+
+#include <sstream>
+#include <string>
+#include <paludis/util/attributes.hh>
+
+/** \file
+ * Stringify functions.
+ *
+ * \ingroup grpstringify
+ */
+
+namespace paludis
+{
+ template <typename T_, typename U_, typename V_>
+ class CountedPtr;
+
+ /**
+ * For use by stringify.
+ *
+ * \ingroup grpstringify
+ */
+ namespace stringify_internals
+ {
+ /**
+ * Check that T_ is a sane type to be stringified.
+ *
+ * \ingroup grpstringify
+ */
+ template <typename T_>
+ struct CheckType
+ {
+ /// Yes, we are a sane type.
+ enum { value = 0 } Value;
+ };
+
+ /**
+ * Check that T_ is a sane type to be stringified, which it isn't
+ * if it's a pointer unless it's a char * pointer.
+ *
+ * \ingroup grpstringify
+ */
+ template <typename T_>
+ struct CheckType<T_ *>
+ {
+ };
+
+ /**
+ * Check that T_ is a sane type to be stringified, which it isn't
+ * if it's a CountedPtr.
+ *
+ * \ingroup grpstringify
+ */
+ template <typename T_, typename U_, typename V_>
+ struct CheckType<CountedPtr<T_, U_, V_> >
+ {
+ };
+
+ /**
+ * Check that T_ is a sane type to be stringified, which it isn't
+ * if it's a pointer unless it's a char * pointer.
+ *
+ * \ingroup grpstringify
+ */
+ template <>
+ struct CheckType<char *>
+ {
+ /// Yes, we are a sane type.
+ enum { value = 0 } Value;
+ };
+ }
+
+ /**
+ * Convert item to a string.
+ *
+ * \ingroup grpstringify
+ */
+ template <typename T_>
+ std::string
+ stringify(const T_ & item)
+ {
+ /* check that we're not trying to stringify a pointer or somesuch */
+ int check_for_stringifying_silly_things
+ PALUDIS_ATTRIBUTE((unused)) = stringify_internals::CheckType<T_>::value;
+
+ std::ostringstream s;
+ s << item;
+ return s.str();
+ }
+
+ /**
+ * Convert item to a string (overload for std::string).
+ *
+ * \ingroup grpstringify
+ */
+ inline std::string
+ stringify(const std::string & item)
+ {
+ return item;
+ }
+
+ /**
+ * Convert item to a string (overload for char).
+ *
+ * \ingroup grpstringify
+ */
+ inline std::string
+ stringify(const char & item)
+ {
+ return std::string(1, item);
+ }
+
+ /**
+ * Convert item to a string (overload for unsigned char).
+ *
+ * \ingroup grpstringify
+ */
+ inline std::string
+ stringify(const unsigned char & item)
+ {
+ return std::string(1, item);
+ }
+
+ /**
+ * Convert item to a string (overload for bool).
+ *
+ * \ingroup grpstringify
+ */
+ inline std::string
+ stringify(const bool & item)
+ {
+ return item ? "true" : "false";
+ }
+
+ /**
+ * Convert item to a string (overload for char *, which isn't a
+ * screwup like other pointers).
+ *
+ * \ingroup grpstringify
+ */
+ inline std::string
+ stringify(const char * const item)
+ {
+ return std::string(item);
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/stringify_TEST.cc b/0.4.0/paludis/util/stringify_TEST.cc
new file mode 100644
index 000000000..5cde52d60
--- /dev/null
+++ b/0.4.0/paludis/util/stringify_TEST.cc
@@ -0,0 +1,129 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/stringify.hh>
+#include <string>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for stringify.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /** \test
+ * Test stringify on int.
+ *
+ * \ingroup grptestcases
+ */
+ struct StringifyIntTests : TestCase
+ {
+ StringifyIntTests() : TestCase("stringify int") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(stringify(0), "0");
+ TEST_CHECK_EQUAL(stringify(1), "1");
+ TEST_CHECK_EQUAL(stringify(99), "99");
+ TEST_CHECK_EQUAL(stringify(-99), "-99");
+ TEST_CHECK_EQUAL(stringify(12345), "12345");
+ }
+ } test_case_stringify_int;
+
+ /** \test
+ * Test stringify on char *.
+ *
+ * \ingroup grptestcases
+ */
+ struct StringifyCharStarTests : TestCase
+ {
+ StringifyCharStarTests() : TestCase("stringify char *") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(stringify("moo"), std::string("moo"));
+ TEST_CHECK_EQUAL(stringify(""), std::string(""));
+ TEST_CHECK(stringify("").empty());
+ TEST_CHECK_EQUAL(stringify(" quack quack "), std::string(" quack quack "));
+ }
+ } test_case_stringify_char_star;
+
+ /** \test
+ * Test stringify on std::string.
+ *
+ * \ingroup grptestcases
+ */
+ struct StringifyStringTests : TestCase
+ {
+ StringifyStringTests() : TestCase("stringify string") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(stringify(std::string("moo")), std::string("moo"));
+ TEST_CHECK_EQUAL(stringify(std::string("")), std::string(""));
+ TEST_CHECK(stringify(std::string("")).empty());
+ TEST_CHECK_EQUAL(stringify(std::string(" quack quack ")), std::string(" quack quack "));
+ }
+ } test_case_stringify_string;
+
+ /** \test
+ * Test stringify on char.
+ *
+ * \ingroup grptestcases
+ */
+ struct StringifyCharTests : TestCase
+ {
+ StringifyCharTests() : TestCase("stringify char") { }
+
+ void run()
+ {
+ char c('a');
+ TEST_CHECK_EQUAL(stringify(c), std::string("a"));
+
+ unsigned char u('a');
+ TEST_CHECK_EQUAL(stringify(u), std::string("a"));
+
+ signed char s('a');
+ TEST_CHECK_EQUAL(stringify(s), std::string("a"));
+ }
+ } test_case_stringify_char;
+
+ /** \test
+ * Test stringify on bool.
+ *
+ * \ingroup grptestcases
+ */
+ struct StringifyBoolTests : TestCase
+ {
+ StringifyBoolTests() : TestCase("stringify bool") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(stringify(true), std::string("true"));
+ TEST_CHECK_EQUAL(stringify(false), std::string("false"));
+ }
+ } test_case_stringify_bool;
+}
+
diff --git a/0.4.0/paludis/util/strip.cc b/0.4.0/paludis/util/strip.cc
new file mode 100644
index 000000000..3a220a120
--- /dev/null
+++ b/0.4.0/paludis/util/strip.cc
@@ -0,0 +1,99 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/exception.hh>
+#include <paludis/util/strip.hh>
+
+/** \file
+ * Implementation of strip things.
+ *
+ * \ingroup grpstrippers
+ */
+
+namespace paludis
+{
+ std::string strip_leading_string(const std::string & s, const std::string & prefix)
+ {
+ try
+ {
+ if (0 == s.compare(0, prefix.length(), prefix))
+ return s.substr(prefix.length());
+ else
+ return s;
+ }
+ catch (const std::exception & e)
+ {
+ throw InternalError(PALUDIS_HERE, "Caught unexpected exception " +
+ stringify(e.what()));
+ }
+ }
+
+ std::string strip_leading(const std::string & s, const std::string & remove)
+ {
+ try
+ {
+ std::string::size_type p(s.find_first_not_of(remove));
+ if (std::string::npos == p)
+ return std::string();
+ else
+ return s.substr(p);
+ }
+ catch (const std::exception & e)
+ {
+ throw InternalError(PALUDIS_HERE, "Caught unexpected exception " +
+ stringify(e.what()));
+ }
+ }
+
+ std::string strip_trailing_string(const std::string & s, const std::string & suffix)
+ {
+ try
+ {
+ if (suffix.length() > s.length())
+ return s;
+ else if (0 == s.compare(s.length() - suffix.length(), suffix.length(), suffix))
+ return s.substr(0, s.length() - suffix.length());
+ else
+ return s;
+ }
+ catch (const std::exception & e)
+ {
+ throw InternalError(PALUDIS_HERE, "Caught unexpected exception " +
+ stringify(e.what()) + " with s='" + s + "', suffix='" + suffix + "'");
+ }
+ }
+
+ std::string strip_trailing(const std::string & s, const std::string & remove)
+ {
+ try
+ {
+ std::string::size_type p(s.find_last_not_of(remove));
+ if (std::string::npos == p)
+ return std::string();
+ else
+ return s.substr(0, p + 1);
+ }
+ catch (const std::exception & e)
+ {
+ throw InternalError(PALUDIS_HERE, "Caught unexpected exception " +
+ stringify(e.what()));
+ }
+ }
+}
+
diff --git a/0.4.0/paludis/util/strip.hh b/0.4.0/paludis/util/strip.hh
new file mode 100644
index 000000000..cc62d1642
--- /dev/null
+++ b/0.4.0/paludis/util/strip.hh
@@ -0,0 +1,127 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_STRIP_HH
+#define PALUDIS_GUARD_PALUDIS_STRIP_HH 1
+
+#include <functional>
+#include <string>
+
+/** \file
+ * Strip functions and adapters.
+ *
+ * \ingroup grpstrippers
+ */
+
+namespace paludis
+{
+ /**
+ * Return a string equal to s minus any leading characters that are
+ * contained in prefix.
+ *
+ * \ingroup grpstrippers
+ */
+ std::string strip_leading_string(const std::string & s, const std::string & prefix);
+
+ /**
+ * Return a string equal to s, minus the string remove if remove occurs at
+ * the start of s.
+ *
+ * \ingroup grpstrippers
+ */
+ std::string strip_leading(const std::string & s, const std::string & remove);
+
+ /**
+ * Return a string equal to s minus any trailing characters that are
+ * contained in suffix.
+ *
+ * \ingroup grpstrippers
+ */
+ std::string strip_trailing_string(const std::string & s, const std::string & suffix);
+
+ /**
+ * Return a string equal to s, minus the string remove if remove occurs at
+ * the end of s.
+ *
+ * \ingroup grpstrippers
+ */
+ std::string strip_trailing(const std::string & s, const std::string & remove);
+
+ /**
+ * Adapt one of the strip_ functions for use as a std::unary_function by
+ * binding a value to the second parameter (avoids the reference to const
+ * issue with std::bind2nd).
+ *
+ * \ingroup grpstrippers
+ */
+ template <std::string (* f_)(const std::string &, const std::string &)>
+ class StripAdapter :
+ public std::unary_function<std::string, const std::string>
+ {
+ private:
+ const std::string _second;
+
+ public:
+ /**
+ * Constructor.
+ */
+ StripAdapter(const std::string & second) :
+ _second(second)
+ {
+ }
+
+ /**
+ * Operation.
+ */
+ std::string operator() (const std::string & first) const
+ {
+ return (*f_)(first, _second);
+ }
+ };
+
+ /**
+ * Adapt strip_leading_string to a functor by binding its second argument.
+ *
+ * \ingroup grpstrippers
+ */
+ typedef StripAdapter<&strip_leading_string> StripLeadingString;
+
+ /**
+ * Adapt strip_leading to a functor by binding its second argument.
+ *
+ * \ingroup grpstrippers
+ */
+ typedef StripAdapter<&strip_leading> StripLeading;
+
+ /**
+ * Adapt strip_trailing_string to a functor by binding its second argument.
+ *
+ * \ingroup grpstrippers
+ */
+ typedef StripAdapter<&strip_trailing_string> StripTrailingString;
+
+ /**
+ * Adapt strip_trailing to a functor by binding its second argument.
+ *
+ * \ingroup grpstrippers
+ */
+ typedef StripAdapter<&strip_trailing> StripTrailing;
+}
+
+#endif
diff --git a/0.4.0/paludis/util/strip_TEST.cc b/0.4.0/paludis/util/strip_TEST.cc
new file mode 100644
index 000000000..54cf287e8
--- /dev/null
+++ b/0.4.0/paludis/util/strip_TEST.cc
@@ -0,0 +1,123 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Mark Loeser <halcy0n@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <algorithm>
+#include <paludis/util/strip.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Test cases for strip.hh.
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test StripLeadingString.
+ *
+ * \ingroup grptestcases
+ */
+ struct StripLeadingStringTest : TestCase
+ {
+ StripLeadingStringTest() : TestCase("StripLeadingString") { }
+
+ void run()
+ {
+ StripLeadingString a("foo");
+
+ TEST_CHECK("bar" == a("foobar"));
+ TEST_CHECK("fishbar" == a("fishbar"));
+ TEST_CHECK("" == a("foo"));
+ TEST_CHECK("fishfoobar" == a("fishfoobar"));
+ TEST_CHECK("blahfoo" == a("blahfoo"));
+ }
+ } test_strip_leading_string;
+
+ /**
+ * \test Test StripLeading.
+ *
+ * \ingroup grptestcases
+ */
+ struct StripLeadingTest : TestCase
+ {
+ StripLeadingTest() : TestCase("StripLeading") {}
+
+ void run()
+ {
+ StripLeading a("foo");
+
+ TEST_CHECK("bar" == a("foobar"));
+ TEST_CHECK("ishbar" == a("fishbar"));
+ TEST_CHECK("" == a("foo"));
+ TEST_CHECK("ishfoobar" == a("fishfoobar"));
+ TEST_CHECK("blahfoo" == a("blahfoo"));
+ }
+ } test_strip_leading;
+
+ /**
+ * \test Test StripTrailingString.
+ *
+ * \ingroup grptestcases
+ */
+ struct StripTrailingStringTest : TestCase
+ {
+ StripTrailingStringTest() : TestCase("StripTrailingString") { }
+
+ void run()
+ {
+ StripTrailingString a("foo");
+
+ TEST_CHECK("foobar" == a("foobar"));
+ TEST_CHECK("fishbar" == a("fishbar"));
+ TEST_CHECK("" == a("foo"));
+ TEST_CHECK("fishfoobar" == a("fishfoobar"));
+ TEST_CHECK("blah" == a("blahfoo"));
+ }
+ } test_strip_trailing_string;
+
+ /**
+ * \test Test StripTrailing.
+ *
+ * \ingroup grptestcases
+ */
+ struct StripTrailingTest : TestCase
+ {
+ StripTrailingTest() : TestCase("StripTrailing") {}
+
+ void run()
+ {
+ StripTrailing a("foo");
+
+ TEST_CHECK("foobar" == a("foobar"));
+ TEST_CHECK("fishbar" == a("fishbar"));
+ TEST_CHECK("" == a("foo"));
+ TEST_CHECK("fishfoobar" == a("fishfoobar"));
+ TEST_CHECK("blah" == a("blahfoo"));
+ }
+ } test_strip_trailing;
+
+}
+
diff --git a/0.4.0/paludis/util/system.cc b/0.4.0/paludis/util/system.cc
new file mode 100644
index 000000000..6cc12b424
--- /dev/null
+++ b/0.4.0/paludis/util/system.cc
@@ -0,0 +1,186 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <cstdlib>
+#include <paludis/util/system.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/stringify.hh>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include "config.h"
+
+/** \file
+ * Implementation of various system utilities.
+ *
+ * \ingroup grpsystem
+ */
+
+using namespace paludis;
+
+namespace
+{
+ /**
+ * Runs a command in a directory if needed, wait for it to terminate
+ * and return its exit status.
+ *
+ * \ingroup grpsystem
+ */
+ int
+ real_run_command(const std::string & cmd, const FSEntry * const fsentry)
+ {
+ pid_t child(fork());
+ if (0 == child)
+ {
+ if (fsentry)
+ if (-1 == chdir(stringify(*fsentry).c_str()))
+ throw RunCommandError("chdir failed: " + stringify(strerror(errno)));
+
+ Log::get_instance()->message(ll_debug, lc_no_context, "execl /bin/sh -c " + cmd);
+ execl("/bin/sh", "sh", "-c", cmd.c_str(), static_cast<char *>(0));
+ throw RunCommandError("execl /bin/sh -c '" + cmd + "' failed:"
+ + stringify(strerror(errno)));
+ }
+ else if (-1 == child)
+ throw RunCommandError("fork failed: " + stringify(strerror(errno)));
+ else
+ {
+ int status(-1);
+ if (-1 == wait(&status))
+ throw RunCommandError("wait failed: " + stringify(strerror(errno)));
+ return status;
+ }
+
+ throw InternalError(PALUDIS_HERE, "should never be reached");
+ }
+}
+
+GetenvError::GetenvError(const std::string & key) throw () :
+ Exception("Environment variable '" + key + "' not set")
+{
+}
+
+RunCommandError::RunCommandError(const std::string & message) throw () :
+ Exception(message)
+{
+}
+
+std::string
+paludis::getenv_with_default(const std::string & key, const std::string & def)
+{
+ const char * const e(std::getenv(key.c_str()));
+ return e ? e : def;
+}
+
+std::string
+paludis::getenv_or_error(const std::string & key)
+{
+ const char * const e(std::getenv(key.c_str()));
+ if (! e)
+ throw GetenvError(key);
+ return e;
+}
+
+namespace
+{
+ /**
+ * Fetch the kernel version, for paludis::kernel_version.
+ *
+ * \ingroup grpsystem
+ */
+ std::string get_kernel_version()
+ {
+ struct utsname u;
+ if (0 != uname(&u))
+ throw InternalError(PALUDIS_HERE, "uname call failed");
+ return u.release;
+ }
+}
+
+std::string
+paludis::kernel_version()
+{
+ static const std::string result(get_kernel_version());
+ return result;
+}
+
+int
+paludis::run_command(const std::string & cmd)
+{
+ return real_run_command(cmd, 0);
+}
+
+int
+paludis::run_command_in_directory(const std::string & cmd, const FSEntry & fsentry)
+{
+ return real_run_command(cmd, &fsentry);
+}
+
+MakeEnvCommand::MakeEnvCommand(const std::string & c,
+ const std::string & a) :
+ cmd(c),
+ args(a)
+{
+}
+
+MakeEnvCommand
+MakeEnvCommand::operator() (const std::string & k,
+ const std::string & v) const
+{
+ std::string vv;
+ for (std::string::size_type p(0) ; p < v.length() ; ++p)
+ if ('\'' == v[p])
+ vv.append("'\"'\"'");
+ else
+ vv.append(v.substr(p, 1));
+
+ return MakeEnvCommand(cmd, args + k + "='" + vv + "' ");
+}
+
+MakeEnvCommand::operator std::string() const
+{
+ return "/usr/bin/env " + args + cmd;
+}
+
+const MakeEnvCommand
+paludis::make_env_command(const std::string & cmd)
+{
+ return MakeEnvCommand(cmd, "");
+}
+
+const std::string
+paludis::make_sandbox_command(const std::string & cmd)
+{
+#if HAVE_SANDBOX
+ if (! getenv_with_default("SANDBOX_ACTIVE", "").empty())
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context,
+ "Already inside sandbox, not spawning another sandbox instance");
+ return cmd;
+ }
+ else
+ return "sandbox " + cmd;
+#else
+ return cmd;
+#endif
+}
+
diff --git a/0.4.0/paludis/util/system.hh b/0.4.0/paludis/util/system.hh
new file mode 100644
index 000000000..99b688122
--- /dev/null
+++ b/0.4.0/paludis/util/system.hh
@@ -0,0 +1,148 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_SYSTEM_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_SYSTEM_HH 1
+
+#include <paludis/util/exception.hh>
+#include <string>
+
+/** \file
+ * Various system utilities.
+ *
+ * \ingroup grpsystem
+ */
+
+namespace paludis
+{
+ class FSEntry;
+
+ /**
+ * Thrown if getenv_or_error fails.
+ *
+ * \ingroup grpexceptions
+ * \ingroup grpsystem
+ */
+ class GetenvError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ GetenvError(const std::string & key) throw ();
+ };
+
+ /**
+ * Thrown if fork, wait or chdir fail when running a command.
+ *
+ * \ingroup grpexceptions
+ * \ingroup grpsystem
+ */
+ class RunCommandError : public Exception
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ RunCommandError(const std::string & message) throw ();
+ };
+
+ /**
+ * Fetch the value of environment variable key, or def if the variable is
+ * not defined.
+ *
+ * \ingroup grpsystem
+ */
+ std::string getenv_with_default(const std::string & key, const std::string & def);
+
+ /**
+ * Fetch the value of environment variable key, or throw a GetenvError if
+ * the variable is not defined.
+ *
+ * \ingroup grpsystem
+ */
+ std::string getenv_or_error(const std::string & key);
+
+ /**
+ * Fetch the kernel version, for $KV.
+ *
+ * \ingroup grpsystem
+ */
+ std::string kernel_version();
+
+ /**
+ * Run a command, wait for it to terminate and return its exit status.
+ *
+ * Use PStream instead if you need to capture stdout.
+ *
+ * \ingroup grpsystem
+ */
+ int run_command(const std::string & cmd);
+
+ /**
+ * Run a command in a directory, wait for it to terminate and return
+ * its exit status.
+ *
+ * \ingroup grpsystem
+ */
+ int run_command_in_directory(const std::string & cmd, const FSEntry & fsentry);
+
+ /**
+ * Make a command that's run in a particular environment.
+ */
+ class MakeEnvCommand
+ {
+ private:
+ std::string cmd;
+ std::string args;
+
+ public:
+ /**
+ * Constructor.
+ */
+ explicit MakeEnvCommand(const std::string &, const std::string &);
+
+ /**
+ * Add some environment.
+ */
+ MakeEnvCommand operator() (const std::string &, const std::string &) const;
+
+ /**
+ * Turn ourself into a command string.
+ */
+ operator std::string() const;
+ };
+
+ /**
+ * Make a command, with environment.
+ *
+ * \ingroup grpsystem
+ */
+ const MakeEnvCommand make_env_command(const std::string & cmd);
+
+ /**
+ * Make a command that is run inside the sandbox, if sandbox is enabled.
+ *
+ * \ingroup grpsystem
+ */
+ const std::string make_sandbox_command(const std::string & cmd);
+}
+
+#endif
+
diff --git a/0.4.0/paludis/util/system_TEST.cc b/0.4.0/paludis/util/system_TEST.cc
new file mode 100644
index 000000000..4e8100a4e
--- /dev/null
+++ b/0.4.0/paludis/util/system_TEST.cc
@@ -0,0 +1,183 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/system.hh>
+#include <paludis/util/pstream.hh>
+#include <paludis/util/fs_entry.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+/** \file
+ * Test cases for system.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+
+ /**
+ * \test Test getenv_with_default.
+ *
+ * \ingroup grptestcases
+ */
+ struct GetenvWithDefaultTest : TestCase
+ {
+ GetenvWithDefaultTest() : TestCase("getenv_with_default") { }
+
+ void run()
+ {
+ TEST_CHECK(! getenv_with_default("HOME", "!").empty());
+ TEST_CHECK_EQUAL(getenv_with_default("HOME", "!").at(0), '/');
+ TEST_CHECK_EQUAL(getenv_with_default("THEREISNOSUCHVARIABLE", "moo"), "moo");
+ }
+ } test_getenv_with_default;
+
+ /**
+ * \test Test getenv_or_error.
+ *
+ * \ingroup grptestcases
+ */
+ struct GetenvOrErrorTest : TestCase
+ {
+ GetenvOrErrorTest() : TestCase("getenv_or_error") { }
+
+ void run()
+ {
+ TEST_CHECK(! getenv_or_error("HOME").empty());
+ TEST_CHECK_THROWS(getenv_or_error("THEREISNOSUCHVARIABLE"), GetenvError);
+ }
+ } test_getenv_or_error;
+
+ /**
+ * \test Test kernel_version.
+ *
+ * \ingroup grptestcases
+ */
+ struct KernelVersionTest : TestCase
+ {
+ KernelVersionTest() : TestCase("kernel version") { }
+
+ void run()
+ {
+ TEST_CHECK(! kernel_version().empty());
+#ifdef linux
+ TEST_CHECK('2' == kernel_version().at(0));
+ TEST_CHECK('.' == kernel_version().at(1));
+#elif defined(__FreeBSD__)
+ TEST_CHECK('6' == kernel_version().at(0));
+ TEST_CHECK('.' == kernel_version().at(1));
+#else
+# error You need to write a sanity test for kernel_version() for your platform.
+#endif
+ }
+ } test_kernel_version;
+
+ /**
+ * \test Test run_command.
+ *
+ * \ingroup grptestcases
+ */
+ struct RunCommandTest : TestCase
+ {
+ RunCommandTest() : TestCase("run_command") { }
+
+ void run()
+ {
+ TEST_CHECK(0 == run_command("true"));
+ TEST_CHECK(0 != run_command("false"));
+ }
+ } test_run_command;
+
+ /**
+ * \test Test run_command_in_directory.
+ *
+ * \ingroup grptestcases
+ */
+ struct RunCommandInDirectoryTest : TestCase
+ {
+ RunCommandInDirectoryTest() : TestCase("run_command_in_directory") { }
+
+ void run()
+ {
+ FSEntry dir("system_TEST_dir");
+ TEST_CHECK(dir.is_directory());
+
+ run_command_in_directory("touch in_directory", dir);
+ TEST_CHECK(FSEntry(dir / "in_directory").exists());
+ run_command_in_directory("rm in_directory", dir);
+ TEST_CHECK(! FSEntry(dir / "in_directory").exists());
+ }
+ } test_run_command_in_directory;
+
+ /**
+ * \test Test make_env_command.
+ *
+ * \ingroup grptestcases
+ */
+ struct MakeEnvCommandTest : TestCase
+ {
+ MakeEnvCommandTest() : TestCase("make_env_command") { }
+
+ void run()
+ {
+ TEST_CHECK(0 != run_command(make_env_command("printenv PALUDUS_TEST_ENV_VAR")));
+ TEST_CHECK(0 == run_command(make_env_command("bash -c '[[ -z $PALUDUS_TEST_ENV_VAR ]]'")));
+ TEST_CHECK(0 == run_command(make_env_command("bash -c '[[ -z $PALUDUS_TEST_ENV_VAR ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "")));
+ TEST_CHECK(0 == run_command(make_env_command("bash -c '[[ -n $PALUDUS_TEST_ENV_VAR ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "foo")));
+ TEST_CHECK(0 != run_command(make_env_command("bash -c '[[ -z $PALUDUS_TEST_ENV_VAR ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "foo")));
+ TEST_CHECK(0 != run_command(make_env_command("bash -c '[[ -n $PALUDUS_TEST_ENV_VAR ]]'")));
+ TEST_CHECK(0 != run_command(make_env_command("bash -c '[[ -n $PALUDUS_TEST_ENV_VAR ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "")));
+ TEST_CHECK(0 == run_command(make_env_command("bash -c '[[ $PALUDUS_TEST_ENV_VAR == foo ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "foo")));
+ TEST_CHECK(0 != run_command(make_env_command("bash -c '[[ $PALUDUS_TEST_ENV_VAR == foo ]]'")));
+ TEST_CHECK(0 != run_command(make_env_command("bash -c '[[ $PALUDUS_TEST_ENV_VAR == foo ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "")));
+ TEST_CHECK(0 != run_command(make_env_command("bash -c '[[ $PALUDUS_TEST_ENV_VAR == foo ]]'")(
+ "PALUDUS_TEST_ENV_VAR", "bar")));
+ }
+ } test_make_env_command;
+
+ /**
+ * \test Test make_env_command with quotes.
+ *
+ * \ingroup grptestcases
+ */
+ struct MakeEnvCommandQuoteTest : TestCase
+ {
+ MakeEnvCommandQuoteTest() : TestCase("make_env_command quotes") { }
+
+ void run()
+ {
+ TEST_CHECK(0 == run_command(make_env_command(
+ "bash -c '[[ x$PALUDUS_TEST_ENV_VAR == \"x....\" ]]'")
+ ("PALUDUS_TEST_ENV_VAR", "....")));
+ TEST_CHECK(0 == run_command(make_env_command(
+ "bash -c '[[ x$PALUDUS_TEST_ENV_VAR == \"x..'\"'\"'..\" ]]'")
+ ("PALUDUS_TEST_ENV_VAR", "..'..")));
+ }
+ } test_make_env_command_quotes;
+}
diff --git a/0.4.0/paludis/util/system_TEST_cleanup.sh b/0.4.0/paludis/util/system_TEST_cleanup.sh
new file mode 100755
index 000000000..1dca3c3a3
--- /dev/null
+++ b/0.4.0/paludis/util/system_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d system_TEST_dir ] ; then
+ rm -fr system_TEST_dir
+else
+ true
+fi
+
diff --git a/0.4.0/paludis/util/system_TEST_setup.sh b/0.4.0/paludis/util/system_TEST_setup.sh
new file mode 100755
index 000000000..d1c9e7623
--- /dev/null
+++ b/0.4.0/paludis/util/system_TEST_setup.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir system_TEST_dir || exit 2
+cd system_TEST_dir || exit 3
diff --git a/0.4.0/paludis/util/test_extras.cc b/0.4.0/paludis/util/test_extras.cc
new file mode 100644
index 000000000..1e6b5bfb3
--- /dev/null
+++ b/0.4.0/paludis/util/test_extras.cc
@@ -0,0 +1,65 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <exception>
+#include <paludis/util/attributes.hh>
+#include <paludis/util/exception.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/stringify.hh>
+#include <sstream>
+#include <test/test_framework.hh>
+
+/** \file
+ * Some extras, for test cases.
+ *
+ * \ingroup grptestcases
+ */
+
+using namespace paludis;
+
+namespace
+{
+ std::string verbose_exception_to_debug_string(
+ const std::exception & e) PALUDIS_ATTRIBUTE((noinline));
+
+ struct C
+ {
+ std::stringstream s;
+
+ C()
+ {
+ test::set_exception_to_debug_string(&verbose_exception_to_debug_string);
+ Log::get_instance()->set_log_stream(&s);
+ }
+ };
+
+ static const C my_c;
+
+ std::string verbose_exception_to_debug_string(const std::exception & e)
+ {
+ const paludis::Exception * ee;
+ if (0 != ((ee = dynamic_cast<const Exception *>(&e))))
+ return stringify(ee->what()) + " (message " + ee->message() +
+ (ee->empty() ? std::string(", no backtrace") :
+ ", backtrace " + ee->backtrace(" -> ")) + ")";
+ else
+ return e.what();
+ }
+}
+
diff --git a/0.4.0/paludis/util/tokeniser.cc b/0.4.0/paludis/util/tokeniser.cc
new file mode 100644
index 000000000..188620087
--- /dev/null
+++ b/0.4.0/paludis/util/tokeniser.cc
@@ -0,0 +1,28 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tokeniser.hh"
+
+using namespace paludis;
+
+WhitespaceTokeniser::WhitespaceTokeniser() :
+ Tokeniser<delim_kind::AnyOfTag, delim_mode::DelimiterTag>(" \t\r\n")
+{
+}
+
diff --git a/0.4.0/paludis/util/tokeniser.hh b/0.4.0/paludis/util/tokeniser.hh
new file mode 100644
index 000000000..f35082660
--- /dev/null
+++ b/0.4.0/paludis/util/tokeniser.hh
@@ -0,0 +1,243 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_TOKENISER_HH
+#define PALUDIS_GUARD_PALUDIS_TOKENISER_HH 1
+
+#include <iterator>
+#include <paludis/util/instantiation_policy.hh>
+#include <string>
+
+/** \file
+ * Declarations for Tokeniser and related utilities.
+ *
+ * \ingroup grptokenise
+ */
+
+namespace paludis
+{
+ /**
+ * Delimiter policy for Tokeniser.
+ *
+ * \ingroup grptokenise
+ */
+ namespace delim_kind
+ {
+ /**
+ * Any of the characters split, and the delimiter is discarded.
+ *
+ * \ingroup grptokenise
+ */
+ struct AnyOfTag
+ {
+ };
+ }
+
+ /**
+ * Delimiter mode for Tokeniser.
+ *
+ * \ingroup grptokenise
+ */
+ namespace delim_mode
+ {
+ /**
+ * Discard the delimiters.
+ *
+ * \ingroup grptokenise
+ */
+ struct DelimiterTag
+ {
+ };
+
+ /**
+ * Keep the delimiters.
+ *
+ * \ingroup grptokenise
+ */
+ struct BoundaryTag
+ {
+ };
+ }
+
+ /**
+ * Tokeniser internal use only.
+ *
+ * \ingroup grptokenise
+ */
+ namespace tokeniser_internals
+ {
+ /**
+ * A Writer handles Tokeniser's writes.
+ *
+ * \ingroup grptokenise
+ */
+ template <typename DelimMode_, typename Char_, typename Iter_>
+ struct Writer;
+
+ /**
+ * A Writer handles Tokeniser's writes (specialisation for
+ * delim_mode::DelimiterTag).
+ *
+ * \ingroup grptokenise
+ */
+ template <typename Char_, typename Iter_>
+ struct Writer<delim_mode::DelimiterTag, Char_, Iter_>
+ {
+ /**
+ * Handle a token.
+ */
+ static void handle_token(const std::basic_string<Char_> & s, Iter_ & i)
+ {
+ *i++ = s;
+ }
+
+ /**
+ * Handle a delimiter.
+ */
+ static void handle_delim(const std::basic_string<Char_> &, const Iter_ &)
+ {
+ }
+ };
+
+ /**
+ * A Writer handles Tokeniser's writes (specialisation for
+ * delim_mode::BoundaryTag).
+ *
+ * \ingroup grptokenise
+ */
+ template <typename Char_, typename Iter_>
+ struct Writer<delim_mode::BoundaryTag, Char_, Iter_>
+ {
+ /**
+ * Handle a token.
+ */
+ static void handle_token(const std::basic_string<Char_> & s, Iter_ & i)
+ {
+ *i++ = s;
+ }
+
+ /**
+ * Handle a delimiter.
+ */
+ static void handle_delim(const std::basic_string<Char_> & s, Iter_ & i)
+ {
+ *i++ = s;
+ }
+ };
+
+ }
+
+ /**
+ * Tokeniser splits up strings into smaller strings.
+ *
+ * \ingroup grptokenise
+ */
+ template <typename DelimKind_, typename DelimMode_, typename Char_ = std::string::value_type>
+ struct Tokeniser;
+
+ /**
+ * Tokeniser: specialisation for delim_kind::AnyOfTag.
+ *
+ * \ingroup grptokenise
+ */
+ template <typename DelimMode_, typename Char_>
+ class Tokeniser<delim_kind::AnyOfTag, DelimMode_, Char_> :
+ private InstantiationPolicy<Tokeniser<delim_kind::AnyOfTag, DelimMode_, Char_>,
+ instantiation_method::NonCopyableTag>
+ {
+ private:
+ const std::basic_string<Char_> _delims;
+
+ public:
+ /**
+ * Constructor.
+ */
+ Tokeniser(const std::basic_string<Char_> & delims) :
+ _delims(delims)
+ {
+ }
+
+ /**
+ * Do the tokenisation.
+ */
+ template <typename Iter_>
+ void tokenise(const std::basic_string<Char_> & s, Iter_ iter) const;
+ };
+
+ template <typename DelimMode_, typename Char_>
+ template <typename Iter_>
+ void
+ Tokeniser<delim_kind::AnyOfTag, DelimMode_, Char_>::tokenise(
+ const std::basic_string<Char_> & s, Iter_ iter) const
+ {
+ typename std::basic_string<Char_>::size_type p(0), old_p(0);
+ bool in_delim((! s.empty()) && std::basic_string<Char_>::npos != _delims.find(s[0]));
+
+ for ( ; p < s.length() ; ++p)
+ {
+ if (in_delim)
+ {
+ if (std::basic_string<Char_>::npos == _delims.find(s[p]))
+ {
+ tokeniser_internals::Writer<DelimMode_, Char_, Iter_>::handle_delim(
+ s.substr(old_p, p - old_p), iter);
+ in_delim = false;
+ old_p = p;
+ }
+ }
+ else
+ {
+ if (std::basic_string<Char_>::npos != _delims.find(s[p]))
+ {
+ tokeniser_internals::Writer<DelimMode_, Char_, Iter_>::handle_token(
+ s.substr(old_p, p - old_p), iter);
+ in_delim = true;
+ old_p = p;
+ }
+ }
+ }
+
+ if (old_p != p)
+ {
+ if (in_delim)
+ tokeniser_internals::Writer<DelimMode_, Char_, Iter_>::handle_delim(
+ s.substr(old_p, p - old_p), iter);
+ else
+ tokeniser_internals::Writer<DelimMode_, Char_, Iter_>::handle_token(
+ s.substr(old_p, p - old_p), iter);
+ }
+ }
+
+ /**
+ * Convenience singleton class for tokenising on whitespace.
+ *
+ * \ingroup grptokenise
+ */
+ class WhitespaceTokeniser :
+ public InstantiationPolicy<WhitespaceTokeniser, instantiation_method::SingletonAtStartupTag>,
+ public Tokeniser<delim_kind::AnyOfTag, delim_mode::DelimiterTag>
+ {
+ friend class InstantiationPolicy<WhitespaceTokeniser, instantiation_method::SingletonAtStartupTag>;
+
+ private:
+ WhitespaceTokeniser();
+ };
+}
+
+#endif
diff --git a/0.4.0/paludis/util/tokeniser_TEST.cc b/0.4.0/paludis/util/tokeniser_TEST.cc
new file mode 100644
index 000000000..a8ea20859
--- /dev/null
+++ b/0.4.0/paludis/util/tokeniser_TEST.cc
@@ -0,0 +1,139 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iterator>
+#include <paludis/util/tokeniser.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for tokeniser.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+namespace test_cases
+{
+ /**
+ * \test Test Tokeniser<AnyOfTag, DelimiterTag>
+ *
+ * \ingroup grptestcases
+ */
+ struct TestTokeniserAD : TestCase
+ {
+ TestTokeniserAD() : TestCase("Tokeniser<AnyOfTag, DelimiterTag>") { }
+
+ void run()
+ {
+ Tokeniser<delim_kind::AnyOfTag, delim_mode::DelimiterTag> t(",.+");
+ std::vector<std::string> tokens;
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("one,two...+...three...", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 3);
+ TEST_CHECK_EQUAL(tokens.at(0), "one");
+ TEST_CHECK_EQUAL(tokens.at(1), "two");
+ TEST_CHECK_EQUAL(tokens.at(2), "three");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("...one,two...+...three", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 3);
+ TEST_CHECK_EQUAL(tokens.at(0), "one");
+ TEST_CHECK_EQUAL(tokens.at(1), "two");
+ TEST_CHECK_EQUAL(tokens.at(2), "three");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("one", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 1);
+ TEST_CHECK_EQUAL(tokens.at(0), "one");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise(".+.,.", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 0);
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 0);
+ tokens.clear();
+ }
+ } test_tokeniser_ad;
+
+ /**
+ * \test Test Tokeniser<AnyOfTag, BoundaryTag>
+ *
+ * \ingroup grptestcases
+ */
+ struct TestTokeniserAB : TestCase
+ {
+ TestTokeniserAB() : TestCase("Tokeniser<AnyOfTag, BoundaryTag>") { }
+
+ void run()
+ {
+ Tokeniser<delim_kind::AnyOfTag, delim_mode::BoundaryTag> t(",.+");
+ std::vector<std::string> tokens;
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("one,two...+...three...", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 6);
+ TEST_CHECK_EQUAL(tokens.at(0), "one");
+ TEST_CHECK_EQUAL(tokens.at(1), ",");
+ TEST_CHECK_EQUAL(tokens.at(2), "two");
+ TEST_CHECK_EQUAL(tokens.at(3), "...+...");
+ TEST_CHECK_EQUAL(tokens.at(4), "three");
+ TEST_CHECK_EQUAL(tokens.at(5), "...");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("...one,two...+...three", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 6);
+ TEST_CHECK_EQUAL(tokens.at(0), "...");
+ TEST_CHECK_EQUAL(tokens.at(1), "one");
+ TEST_CHECK_EQUAL(tokens.at(2), ",");
+ TEST_CHECK_EQUAL(tokens.at(3), "two");
+ TEST_CHECK_EQUAL(tokens.at(4), "...+...");
+ TEST_CHECK_EQUAL(tokens.at(5), "three");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("one", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 1);
+ TEST_CHECK_EQUAL(tokens.at(0), "one");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise(".+.,.", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 1);
+ TEST_CHECK_EQUAL(tokens.at(0), ".+.,.");
+ tokens.clear();
+
+ TEST_CHECK(tokens.empty());
+ t.tokenise("", std::back_inserter(tokens));
+ TEST_CHECK_EQUAL(tokens.size(), 0);
+ tokens.clear();
+ }
+ } test_tokeniser_ab;
+}
diff --git a/0.4.0/paludis/util/util.hh.m4 b/0.4.0/paludis/util/util.hh.m4
new file mode 100644
index 000000000..4a468b395
--- /dev/null
+++ b/0.4.0/paludis/util/util.hh.m4
@@ -0,0 +1,42 @@
+#if 0
+ifdef(`__gnu__',`',`errprint(`This is not GNU m4...
+')m4exit(1)') include(`misc/generated-file.txt')
+dnl vim: set ft=cpp et sw=4 sts=4 :
+#endif
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_UTIL_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_UTIL_HH 1
+
+/** \file
+ * Master include file for util.
+ */
+
+define(`addhh', `dnl
+#include <paludis/util/$1.hh>
+')dnl
+define(`addthis', `ifelse(`$2', `hh', `addhh(`$1')',`')')
+define(`add', `addthis(`$1',`$2')addthis(`$1',`$3')addthis(`$1',`$4')dnl
+addthis(`$1',`$5')addthis(`$1',`$6')')dnl
+
+include(`paludis/util/files.m4')
+
+#endif
+
+
diff --git a/0.4.0/paludis/util/validated.hh b/0.4.0/paludis/util/validated.hh
new file mode 100644
index 000000000..b6f72fcc1
--- /dev/null
+++ b/0.4.0/paludis/util/validated.hh
@@ -0,0 +1,111 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_VALIDATED_HH
+#define PALUDIS_GUARD_PALUDIS_VALIDATED_HH 1
+
+#include <ostream>
+#include <paludis/util/comparison_policy.hh>
+
+/** \file
+ * Validated declarations.
+ *
+ * \ingroup grpvalidated
+ */
+
+namespace paludis
+{
+ /**
+ * A Validated wraps a particular class instance, ensuring that it always
+ * meets certain validation criteria.
+ *
+ * \ingroup grpvalidated
+ */
+ template <typename ValidatedDataType_, typename Validator_>
+ class Validated : public ComparisonPolicy<
+ Validated<ValidatedDataType_, Validator_>,
+ comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<ValidatedDataType_> >
+ {
+ private:
+ ValidatedDataType_ _value;
+
+ public:
+ /**
+ * Copy constructor (no validation needed).
+ */
+ Validated(const Validated<ValidatedDataType_, Validator_> & other) :
+ ComparisonPolicy<
+ Validated<ValidatedDataType_, Validator_>,
+ comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<ValidatedDataType_> >
+ (other),
+ _value(other._value)
+ {
+ }
+
+ /**
+ * Constructor (validation needed).
+ */
+ explicit Validated(const ValidatedDataType_ & value) :
+ ComparisonPolicy<
+ Validated<ValidatedDataType_, Validator_>,
+ comparison_mode::FullComparisonTag,
+ comparison_method::CompareByMemberTag<ValidatedDataType_> >
+ (&Validated<ValidatedDataType_, Validator_>::_value),
+ _value(value)
+ {
+ Validator_::validate(_value);
+ }
+
+ /**
+ * Assignment (no validation needed).
+ */
+ const Validated<ValidatedDataType_, Validator_> & operator=
+ (const Validated<ValidatedDataType_,Validator_> & other)
+ {
+ _value = other._value;
+ return *this;
+ }
+
+ /**
+ * Fetch to our ValidatedDataType_. This should not be a cast
+ * operator to avoid problems with ambiguous comparison operators.
+ */
+ const ValidatedDataType_ & data() const
+ {
+ return _value;
+ }
+ };
+
+ /**
+ * Writing a Validated instance to a stream is done by its data.
+ *
+ * \ingroup grpvalidated
+ */
+ template <typename D_, typename V_>
+ std::ostream &
+ operator<< (std::ostream & s, const Validated<D_, V_> & v)
+ {
+ s << v.data();
+ return s;
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/validated_TEST.cc b/0.4.0/paludis/util/validated_TEST.cc
new file mode 100644
index 000000000..626f96869
--- /dev/null
+++ b/0.4.0/paludis/util/validated_TEST.cc
@@ -0,0 +1,126 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/validated.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+/** \file
+ * Test cases for validated.hh .
+ *
+ * \ingroup grpvalidated
+ */
+
+#ifndef DOXYGEN
+struct PositiveEvenValidator
+{
+ struct NotValid
+ {
+ };
+
+ static void validate(int value)
+ {
+ if ((value < 0) || (value % 2))
+ throw NotValid();
+ }
+};
+
+typedef Validated<int, PositiveEvenValidator> PositiveEven;
+#endif
+
+namespace test_cases
+{
+ /**
+ * \test Validated<PositiveEven> tests.
+ *
+ * \ingroup grpvalidated
+ */
+ struct ValidatedPositiveEvenTests : TestCase
+ {
+ ValidatedPositiveEvenTests() : TestCase("Validated<PositiveEven> tests") { }
+
+ void run()
+ {
+ PositiveEven v(2);
+ TEST_CHECK_EQUAL(v, PositiveEven(2));
+ v = PositiveEven(4);
+ TEST_CHECK_EQUAL(v, PositiveEven(4));
+ TEST_CHECK_THROWS(((v = PositiveEven(5))), PositiveEvenValidator::NotValid);
+ TEST_CHECK_EQUAL(v, PositiveEven(4));
+ TEST_CHECK_THROWS(PositiveEven w(5), PositiveEvenValidator::NotValid);
+ }
+ } test_validated_positive_even;
+
+ /**
+ * \test Validated<PositiveEven> comparison tests.
+ *
+ * \ingroup grpvalidated
+ */
+ struct ValidatedPositiveEvenComparisonTests : TestCase
+ {
+ ValidatedPositiveEvenComparisonTests() :
+ TestCase("Validated<PositiveEven> comparison tests") { }
+
+ void run()
+ {
+ PositiveEven v2(2);
+ PositiveEven v4(4);
+ PositiveEven v4b(4);
+
+ TEST_CHECK(v2 < v4);
+ TEST_CHECK(v2 <= v4);
+ TEST_CHECK(! (v2 == v4));
+ TEST_CHECK(v2 != v4);
+ TEST_CHECK(! (v2 >= v4));
+ TEST_CHECK(! (v2 > v4));
+
+ TEST_CHECK(! (v4 < v2));
+ TEST_CHECK(! (v4 <= v2));
+ TEST_CHECK(! (v4 == v2));
+ TEST_CHECK(v4 != v2);
+ TEST_CHECK(v4 >= v2);
+ TEST_CHECK(v4 > v2);
+
+ TEST_CHECK(! (v2 < v2));
+ TEST_CHECK(v2 <= v2);
+ TEST_CHECK(v2 == v2);
+ TEST_CHECK(! (v2 != v2));
+ TEST_CHECK(v2 >= v2);
+ TEST_CHECK(! (v2 > v2));
+
+ TEST_CHECK(! (v4 < v4));
+ TEST_CHECK(v4 <= v4);
+ TEST_CHECK(v4 == v4);
+ TEST_CHECK(! (v4 != v4));
+ TEST_CHECK(v4 >= v4);
+ TEST_CHECK(! (v4 > v4));
+
+ TEST_CHECK(! (v4 < v4b));
+ TEST_CHECK(v4 <= v4b);
+ TEST_CHECK(v4 == v4b);
+ TEST_CHECK(! (v4 != v4b));
+ TEST_CHECK(v4 >= v4b);
+ TEST_CHECK(! (v4 > v4b));
+ }
+ } test_validated_positive_even_comparisons;
+}
+
diff --git a/0.4.0/paludis/util/virtual_constructor.hh b/0.4.0/paludis/util/virtual_constructor.hh
new file mode 100644
index 000000000..503ebf97f
--- /dev/null
+++ b/0.4.0/paludis/util/virtual_constructor.hh
@@ -0,0 +1,243 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_VIRTUAL_CONSTRUCTOR_HH
+#define PALUDIS_GUARD_PALUDIS_VIRTUAL_CONSTRUCTOR_HH 1
+
+#include <paludis/util/exception.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/iterator.hh>
+
+#include <algorithm>
+#include <vector>
+
+/** \file
+ * Declarations for VirtualConstructor and related classes.
+ *
+ * \ingroup grpvc
+ */
+
+namespace paludis
+{
+ /**
+ * Behaviour policy classes for what to do if an appropriate constructor
+ * cannot be found for a VirtualConstructor::find_maker call.
+ *
+ * \ingroup grpvc
+ */
+ namespace virtual_constructor_not_found
+ {
+ /**
+ * Throw an exception of type ExceptionType_, which should have a
+ * constructor that takes a single parameter of KeyType_.
+ *
+ * \ingroup grpvc
+ */
+ template <typename ExceptionType_>
+ struct ThrowException
+ {
+ /**
+ * Internal use: provide handle_not_found.
+ *
+ * \ingroup grpvc
+ */
+ template <typename KeyType_, typename ValueType_>
+ struct Parent
+ {
+ /**
+ * Internal use: called when we cannot find a key.
+ */
+ ValueType_ handle_not_found(const KeyType_ & k) const PALUDIS_ATTRIBUTE((noreturn));
+ };
+ };
+
+ template <typename ExceptionType_>
+ template <typename KeyType_, typename ValueType_>
+ ValueType_
+ ThrowException<ExceptionType_>::Parent<KeyType_, ValueType_>::handle_not_found(
+ const KeyType_ & k) const
+ {
+ throw ExceptionType_(k);
+ }
+ }
+
+ /**
+ * For internal use by VirtualConstructor.
+ *
+ * \ingroup grpvc
+ */
+ namespace virtual_constructor_internals
+ {
+ /**
+ * Comparator class for our entries.
+ *
+ * \ingroup grpvc
+ */
+ template <typename First_, typename Second_>
+ struct ComparePairByFirst
+ {
+ /**
+ * Compare, with the entry on the LHS.
+ */
+ bool operator() (const std::pair<First_, Second_> & a, const First_ & b) const
+ {
+ return a.first < b;
+ }
+
+ /**
+ * Compare, with the entry on the RHS.
+ */
+ bool operator() (const First_ & a, const std::pair<First_, Second_> & b) const
+ {
+ return a < b.first;
+ }
+ };
+ }
+
+ /**
+ * A VirtualConstructor can be used where a mapping between the value of
+ * some key type (often a string) to the construction of some kind of
+ * class (possibly via a functor) is required.
+ *
+ * \ingroup grpvc
+ */
+ template <typename KeyType_, typename ValueType_, typename NotFoundBehaviour_>
+ class VirtualConstructor :
+ public NotFoundBehaviour_::template Parent<KeyType_, ValueType_>,
+ public InstantiationPolicy<VirtualConstructor<KeyType_, ValueType_, NotFoundBehaviour_>,
+ instantiation_method::SingletonAsNeededTag>
+ {
+ friend class InstantiationPolicy<
+ VirtualConstructor<KeyType_, ValueType_, NotFoundBehaviour_>,
+ instantiation_method::SingletonAsNeededTag>;
+
+ private:
+ VirtualConstructor()
+ {
+ }
+
+ protected:
+ /**
+ * Our entries, sorted.
+ */
+ std::vector<std::pair<KeyType_, ValueType_> > entries;
+
+ public:
+ /**
+ * The type of our key.
+ */
+ typedef KeyType_ KeyType;
+
+ /**
+ * The type of our value.
+ */
+ typedef ValueType_ ValueType;
+
+ /**
+ * The behaviour policy for when a key is not found.
+ */
+ typedef NotFoundBehaviour_ NotFoundBehaviour;
+
+ /**
+ * Find a value for the specified key, or perform the appropriate
+ * NotFoundBehaviour.
+ */
+ ValueType_ find_maker(const KeyType_ & k) const;
+
+ /**
+ * Convenience alias for find_maker.
+ */
+ ValueType_ operator[] (const KeyType_ & k) const
+ {
+ return find_maker(k);
+ }
+
+ /**
+ * Register a new maker (should usually be called by the
+ * RegisterMaker child class.
+ */
+ void register_maker(const KeyType_ & k, const ValueType_ & v);
+
+ /**
+ * Copy out our keys.
+ */
+ template <typename T_>
+ void copy_keys(T_ out_iter) const;
+
+ /**
+ * An instance of this class registers a new maker with the
+ * specified key.
+ *
+ * \ingroup grpvc
+ */
+ struct RegisterMaker
+ {
+ /**
+ * Constructor.
+ */
+ RegisterMaker(const KeyType_ & k, const ValueType_ & v)
+ {
+ VirtualConstructor<KeyType_, ValueType_, NotFoundBehaviour_>::get_instance()->
+ register_maker(k, v);
+ }
+ };
+ };
+
+ template <typename KeyType_, typename ValueType_, typename NotFoundBehaviour_>
+ ValueType_
+ VirtualConstructor<KeyType_, ValueType_, NotFoundBehaviour_>::find_maker(
+ const KeyType_ & k) const
+ {
+ std::pair<
+ typename std::vector<std::pair<KeyType_, ValueType_> >::const_iterator,
+ typename std::vector<std::pair<KeyType_, ValueType_> >::const_iterator > m(
+ std::equal_range(entries.begin(), entries.end(), k,
+ virtual_constructor_internals::ComparePairByFirst<KeyType_, ValueType_>()));
+ if (m.first == m.second)
+ return this->handle_not_found(k);
+ else
+ return m.first->second;
+ }
+
+ template <typename KeyType_, typename ValueType_, typename NotFoundBehaviour_>
+ void
+ VirtualConstructor<KeyType_, ValueType_, NotFoundBehaviour_>::register_maker(
+ const KeyType_ & k, const ValueType_ & v)
+ {
+ std::pair<
+ typename std::vector<std::pair<KeyType_, ValueType_> >::iterator,
+ typename std::vector<std::pair<KeyType_, ValueType_> >::iterator > m(
+ std::equal_range(entries.begin(), entries.end(), k,
+ virtual_constructor_internals::ComparePairByFirst<KeyType_, ValueType_>()));
+ if (m.first == m.second)
+ entries.insert(m.first, std::make_pair(k, v));
+ }
+
+ template <typename KeyType_, typename ValueType_, typename NotFoundBehaviour_>
+ template <typename T_>
+ void
+ VirtualConstructor<KeyType_, ValueType_, NotFoundBehaviour_>::copy_keys(T_ out_iter) const
+ {
+ std::copy(entries.begin(), entries.end(), TransformInsertIterator<
+ T_, SelectFirst<KeyType_, ValueType_> >(out_iter));
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/virtual_constructor_TEST.cc b/0.4.0/paludis/util/virtual_constructor_TEST.cc
new file mode 100644
index 000000000..2273dde9e
--- /dev/null
+++ b/0.4.0/paludis/util/virtual_constructor_TEST.cc
@@ -0,0 +1,190 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/counted_ptr.hh>
+#include <paludis/util/virtual_constructor.hh>
+#include <set>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+/** \file
+ * Test cases for VirtualConstructor.
+ *
+ * \ingroup grptestcases
+ */
+
+using namespace test;
+using namespace paludis;
+
+#ifndef DOXYGEN
+enum CookieSize
+{
+ cs_small,
+ cs_large
+};
+
+class Cookie :
+ public InternalCounted<Cookie>
+{
+ private:
+ CookieSize _size;
+
+ protected:
+ Cookie(CookieSize size) :
+ _size(size)
+ {
+ };
+
+ public:
+ virtual std::string flavour() const = 0;
+
+ virtual ~Cookie()
+ {
+ }
+
+ CookieSize size() const
+ {
+ return _size;
+ }
+};
+
+struct NoCookie
+{
+ NoCookie(const std::string &)
+ {
+ }
+};
+
+typedef VirtualConstructor<std::string,
+ CountedPtr<Cookie> (*) (CookieSize),
+ virtual_constructor_not_found::ThrowException<NoCookie> > CookieMaker;
+
+class ChocolateChipCookie : public Cookie
+{
+ public:
+ ChocolateChipCookie(CookieSize size) :
+ Cookie(size)
+ {
+ }
+
+ std::string flavour() const
+ {
+ return "Chocolate Chip";
+ }
+
+ static CountedPtr<Cookie> make(CookieSize size)
+ {
+ return CountedPtr<Cookie>(new ChocolateChipCookie(size));
+ }
+};
+
+CookieMaker::RegisterMaker register_chocolate_chip("chocolate chip", &ChocolateChipCookie::make);
+
+class GingerCookie : public Cookie
+{
+ private:
+ bool _with_crunchy_bits;
+
+ public:
+ GingerCookie(CookieSize size, bool with_crunchy_bits) :
+ Cookie(size),
+ _with_crunchy_bits(with_crunchy_bits)
+ {
+ }
+
+ std::string flavour() const
+ {
+ return _with_crunchy_bits ? "Crunchy Ginger" : "Ginger";
+ }
+
+ bool with_crunchy_bits() const
+ {
+ return _with_crunchy_bits;
+ }
+
+ static CountedPtr<Cookie> make(CookieSize size)
+ {
+ return CountedPtr<Cookie>(new GingerCookie(size, false));
+ }
+
+ static CountedPtr<Cookie> make_crunchy(CookieSize size)
+ {
+ return CountedPtr<Cookie>(new GingerCookie(size, true));
+ }
+};
+
+CookieMaker::RegisterMaker register_ginger("ginger", &GingerCookie::make);
+CookieMaker::RegisterMaker register_crunchy_ginger("crunchy ginger", &GingerCookie::make_crunchy);
+
+#endif
+
+namespace test_cases
+{
+ /**
+ * \test Test VirtualConstructor.
+ *
+ * \ingroup grptestcases
+ */
+ struct VirtualConstructorTest : TestCase
+ {
+ VirtualConstructorTest() : TestCase("virtual constructor") { }
+
+ void run()
+ {
+ TEST_CHECK_EQUAL(CookieMaker::get_instance()->find_maker(
+ "chocolate chip")(cs_large)->flavour(), "Chocolate Chip");
+ TEST_CHECK_EQUAL(CookieMaker::get_instance()->find_maker(
+ "chocolate chip")(cs_large)->size(), cs_large);
+ TEST_CHECK_EQUAL(CookieMaker::get_instance()->find_maker(
+ "chocolate chip")(cs_small)->size(), cs_small);
+
+ TEST_CHECK_EQUAL((*CookieMaker::get_instance())["ginger"](cs_small)->flavour(),
+ "Ginger");
+ TEST_CHECK_EQUAL((*CookieMaker::get_instance())["crunchy ginger"](cs_small)->flavour(),
+ "Crunchy Ginger");
+
+ TEST_CHECK_THROWS(CookieMaker::get_instance()->find_maker(
+ "gerbil")(cs_large)->flavour(), NoCookie);
+ }
+ } test_virtual_constructor;
+
+ /**
+ * \test Test VirtualConstructor keys
+ *
+ * \ingroup grptestcases
+ */
+ struct VirtualConstructorKeysTest : TestCase
+ {
+ VirtualConstructorKeysTest() : TestCase("virtual constructor keys") { }
+
+ void run()
+ {
+ std::set<std::string> keys;
+
+ TEST_CHECK(keys.empty());
+ CookieMaker::get_instance()->copy_keys(std::inserter(keys, keys.begin()));
+ TEST_CHECK(! keys.empty());
+ TEST_CHECK(keys.end() != keys.find("crunchy ginger"));
+ TEST_CHECK(keys.end() != keys.find("ginger"));
+ TEST_CHECK(keys.end() != keys.find("chocolate chip"));
+ TEST_CHECK(keys.end() == keys.find("gerbil"));
+ }
+ } test_virtual_constructor_keys;
+}
+
diff --git a/0.4.0/paludis/util/visitor.hh b/0.4.0/paludis/util/visitor.hh
new file mode 100644
index 000000000..33a4d6eb6
--- /dev/null
+++ b/0.4.0/paludis/util/visitor.hh
@@ -0,0 +1,297 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_VISITOR_HH
+#define PALUDIS_GUARD_PALUDIS_VISITOR_HH 1
+
+/** \file
+ * Declares the Visitor and related classes.
+ *
+ * \ingroup grpvisitor
+ */
+
+namespace paludis
+{
+ template <typename NodePtrType_>
+ class Visits;
+
+ /**
+ * Internal use for Visitor classes.
+ *
+ * \ingroup grpvisitor
+ */
+ namespace visitor_internals
+ {
+ /**
+ * Used as a default parameter when no type is provided. The n_
+ * parameter is used to avoid inheriting the same class more than once
+ * from a single parent.
+ *
+ * \ingroup grpvisitor
+ */
+ template <unsigned n_>
+ struct NoType
+ {
+ };
+
+ /**
+ * Make a pointer to a const.
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename>
+ struct MakePointerToConst;
+
+ /**
+ * Make a pointer to a const (specialisation for non-const pointers).
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename T_>
+ struct MakePointerToConst<T_ *>
+ {
+ /**
+ * Our type.
+ */
+ typedef const T_ * Type;
+ };
+
+ /**
+ * Interface: visit a class of NodePtrType_.
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename NodePtrType_>
+ class Visits
+ {
+ protected:
+ /**
+ * Destructor.
+ */
+ virtual ~Visits()
+ {
+ }
+
+ public:
+ /**
+ * Visit a node of the specified type.
+ */
+ virtual void visit(NodePtrType_ const) = 0;
+ };
+
+ /**
+ * Interface: don't visit NoType things.
+ *
+ * \ingroup grpvisitor
+ */
+ template <unsigned n_>
+ class Visits<const visitor_internals::NoType<n_> * >
+ {
+ protected:
+ /**
+ * Destructor.
+ */
+ ~Visits()
+ {
+ }
+ };
+
+ /**
+ * Interface: don't visit NoType things.
+ *
+ * \ingroup grpvisitor
+ */
+ template <unsigned n_>
+ class Visits<visitor_internals::NoType<n_> * >
+ {
+ protected:
+ ~Visits()
+ {
+ }
+ };
+ }
+
+ /**
+ * A class that inherits virtually from VisitableInterface can accept a
+ * visitor that is descended from one of the VisitorType_ subclasses.
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename VisitorType_>
+ class VisitableInterface
+ {
+ protected:
+ /**
+ * Destructor.
+ */
+ virtual ~VisitableInterface()
+ {
+ }
+
+ public:
+ /**
+ * Accept a visitor.
+ */
+ virtual void accept(typename VisitorType_::Visitor * const) = 0;
+
+ /**
+ * Accept a constant visitor.
+ */
+ virtual void accept(typename VisitorType_::ConstVisitor * const) const = 0;
+ };
+
+ /**
+ * A class that inherits (non-virtually) from Visitable provides an
+ * implementation of VisitableInterface.
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename OurType_, typename VisitorType_>
+ class Visitable :
+ public virtual VisitableInterface<VisitorType_>
+ {
+ protected:
+ /**
+ * Destructor.
+ */
+ virtual ~Visitable()
+ {
+ }
+
+ public:
+ virtual void accept(typename VisitorType_::Visitor * const v)
+ {
+ static_cast<visitor_internals::Visits<OurType_ *> *>(v)->visit(
+ static_cast<OurType_ *>(this));
+ }
+
+ virtual void accept(typename VisitorType_::ConstVisitor * const v) const
+ {
+ static_cast<visitor_internals::Visits<const OurType_ *> *>(v)->visit(
+ static_cast<const OurType_ *>(this));
+ }
+ };
+
+ /**
+ * Create the base classes for constant and non-constant visitors to the
+ * specified node types.
+ *
+ * \ingroup grpvisitor
+ */
+ template <
+ typename N1_,
+ typename N2_ = visitor_internals::NoType<2> *,
+ typename N3_ = visitor_internals::NoType<3> *,
+ typename N4_ = visitor_internals::NoType<4> *,
+ typename N5_ = visitor_internals::NoType<5> *,
+ typename N6_ = visitor_internals::NoType<6> *,
+ typename N7_ = visitor_internals::NoType<7> *,
+ typename N8_ = visitor_internals::NoType<8> *,
+ typename N9_ = visitor_internals::NoType<9> *>
+ class VisitorTypes
+ {
+ private:
+ VisitorTypes();
+
+ public:
+ /**
+ * A ConstVisitor descendent visits nodes via a const pointer.
+ */
+ class ConstVisitor :
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N1_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N2_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N3_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N4_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N5_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N6_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N7_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N8_>::Type>,
+ public visitor_internals::Visits<typename visitor_internals::MakePointerToConst<N9_>::Type>
+ {
+ protected:
+ ~ConstVisitor()
+ {
+ }
+ };
+
+ /**
+ * A Visitor descendent visits nodes via a non-const pointer.
+ */
+ class Visitor :
+ public visitor_internals::Visits<N1_>,
+ public visitor_internals::Visits<N2_>,
+ public visitor_internals::Visits<N3_>,
+ public visitor_internals::Visits<N4_>,
+ public visitor_internals::Visits<N5_>,
+ public visitor_internals::Visits<N6_>,
+ public visitor_internals::Visits<N7_>,
+ public visitor_internals::Visits<N8_>,
+ public visitor_internals::Visits<N9_>
+ {
+ protected:
+ ~Visitor()
+ {
+ }
+ };
+ };
+
+ /**
+ * Functor: simplify calling accept on a visitor when we have a container of
+ * pointers to nodes.
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename VisitorPointer_>
+ class AcceptVisitor
+ {
+ private:
+ VisitorPointer_ * const _p;
+
+ public:
+ /**
+ * Constructor.
+ */
+ AcceptVisitor(VisitorPointer_ * const p) :
+ _p(p)
+ {
+ }
+
+ /**
+ * Operator.
+ */
+ template <typename T_>
+ void operator() (T_ t) const
+ {
+ t->accept(_p);
+ }
+ };
+
+ /**
+ * Convenience function: create an AcceptVisitor.
+ *
+ * \ingroup grpvisitor
+ */
+ template <typename VisitorPointer_>
+ AcceptVisitor<VisitorPointer_> accept_visitor(VisitorPointer_ * const p)
+ {
+ return AcceptVisitor<VisitorPointer_>(p);
+ }
+}
+
+#endif
diff --git a/0.4.0/paludis/util/visitor_TEST.cc b/0.4.0/paludis/util/visitor_TEST.cc
new file mode 100644
index 000000000..25e020ecc
--- /dev/null
+++ b/0.4.0/paludis/util/visitor_TEST.cc
@@ -0,0 +1,168 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <algorithm>
+#include <paludis/util/deleter.hh>
+#include <paludis/util/iterator.hh>
+#include <paludis/util/visitor.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <vector>
+
+using namespace paludis;
+using namespace test;
+
+/** \file
+ * Test cases for visitor.hh .
+ *
+ * \ingroup grptestcases
+ */
+
+#ifndef DOXYGEN
+
+class Node;
+class FooNode;
+class BarNode;
+
+typedef VisitorTypes<FooNode *, BarNode *> NodeVisitorTypes;
+
+struct Node :
+ virtual VisitableInterface<NodeVisitorTypes>
+{
+};
+
+struct FooNode :
+ Node,
+ Visitable<FooNode, NodeVisitorTypes>
+{
+ std::string c_foo() const
+ {
+ return "c_foo";
+ }
+
+ std::string foo()
+ {
+ return "foo";
+ }
+};
+
+struct BarNode :
+ Node,
+ Visitable<BarNode, NodeVisitorTypes>
+{
+ std::string c_bar() const
+ {
+ return "c_bar";
+ }
+
+ std::string bar()
+ {
+ return "bar";
+ }
+};
+
+struct NodeCVisitor :
+ NodeVisitorTypes::ConstVisitor
+{
+ std::string r;
+
+ virtual void visit(const FooNode * const f)
+ {
+ r.append(f->c_foo());
+ }
+
+ virtual void visit(const BarNode * const b)
+ {
+ r.append(b->c_bar());
+ }
+};
+
+struct NodeVisitor :
+ NodeVisitorTypes::Visitor
+{
+ std::string r;
+
+ virtual void visit(FooNode * const f)
+ {
+ r.append(f->foo());
+ }
+
+ virtual void visit(BarNode * const b)
+ {
+ r.append(b->bar());
+ }
+};
+
+#endif
+
+namespace test_cases
+{
+ /**
+ * \test Test const visitors.
+ *
+ * \ingroup grptestcases
+ */
+ struct ConstVisitorTest : TestCase
+ {
+ ConstVisitorTest() : TestCase("const visitor") { }
+
+ void run()
+ {
+ std::vector<Node *> v;
+
+ v.push_back(new FooNode);
+ v.push_back(new BarNode);
+ v.push_back(new FooNode);
+
+ NodeCVisitor c;
+ TEST_CHECK_EQUAL(c.r, "");
+ std::for_each(v.begin(), v.end(), accept_visitor(&c));
+ TEST_CHECK_EQUAL(c.r, "c_fooc_barc_foo");
+
+ std::for_each(v.begin(), v.end(), Deleter());
+ }
+ } test_const_visitor;
+
+ /**
+ * \test Test non-const visitors.
+ *
+ * \ingroup grptestcases
+ */
+ struct VisitorTest : TestCase
+ {
+ VisitorTest() : TestCase("visitor") { }
+
+ void run()
+ {
+ std::vector<Node *> v;
+
+ v.push_back(new FooNode);
+ v.push_back(new BarNode);
+ v.push_back(new FooNode);
+
+ NodeVisitor c;
+ TEST_CHECK_EQUAL(c.r, "");
+ std::for_each(v.begin(), v.end(), accept_visitor(&c));
+ TEST_CHECK_EQUAL(c.r, "foobarfoo");
+
+ std::for_each(v.begin(), v.end(), Deleter());
+ }
+ } test_visitor;
+}
+