aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar David Leverton <levertond@googlemail.com> 2007-10-09 19:56:39 +0000
committerAvatar David Leverton <levertond@googlemail.com> 2007-10-09 19:56:39 +0000
commit91ec1b95f569fe36232645356ce26c598582110f (patch)
treedbe4382abb9ed4a428dbb08cb5c702ea2c24c110
parentc21ac6cc7c435dd5691b68423aaa81236dde6103 (diff)
downloadpaludis-91ec1b95f569fe36232645356ce26c598582110f.tar.gz
paludis-91ec1b95f569fe36232645356ce26c598582110f.tar.xz
Add reconcilio, a replacement for revdep-rebuild, with littlelf library by Tiziano Müller.
-rw-r--r--configure.ac9
-rwxr-xr-xpaludis/repositories/e/ebuild/ebuild.bash1
-rw-r--r--src/clients/reconcilio/Makefile.am94
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/Makefile.am40
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.cc382
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.hh78
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/configuration.cc303
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/configuration.hh59
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/configuration_TEST.cc71
-rwxr-xr-xsrc/clients/reconcilio/broken_linkage_finder/configuration_TEST_cleanup.sh11
-rwxr-xr-xsrc/clients/reconcilio/broken_linkage_finder/configuration_TEST_setup.sh28
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.cc270
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.hh55
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.cc163
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.hh46
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/linkage_checker.cc31
-rw-r--r--src/clients/reconcilio/broken_linkage_finder/linkage_checker.hh54
-rw-r--r--src/clients/reconcilio/command_line.cc97
-rw-r--r--src/clients/reconcilio/command_line.hh68
-rw-r--r--src/clients/reconcilio/fix_linkage.cc143
-rw-r--r--src/clients/reconcilio/fix_linkage.hh29
-rw-r--r--src/clients/reconcilio/install.cc131
-rw-r--r--src/clients/reconcilio/install.hh33
-rw-r--r--src/clients/reconcilio/littlelf/Elf.cc237
-rw-r--r--src/clients/reconcilio/littlelf/Elf.hh103
-rw-r--r--src/clients/reconcilio/littlelf/ElfDynamicSection.cc285
-rw-r--r--src/clients/reconcilio/littlelf/ElfDynamicSection.hh210
-rw-r--r--src/clients/reconcilio/littlelf/ElfRelocationSection.cc84
-rw-r--r--src/clients/reconcilio/littlelf/ElfRelocationSection.hh95
-rw-r--r--src/clients/reconcilio/littlelf/ElfSections.cc81
-rw-r--r--src/clients/reconcilio/littlelf/ElfSections.hh147
-rw-r--r--src/clients/reconcilio/littlelf/ElfSymbolSection.cc155
-rw-r--r--src/clients/reconcilio/littlelf/ElfSymbolSection.hh85
-rw-r--r--src/clients/reconcilio/littlelf/ElfTypes.hh68
-rw-r--r--src/clients/reconcilio/littlelf/Makefile.am32
-rw-r--r--src/clients/reconcilio/man_reconcilio.cc78
-rw-r--r--src/clients/reconcilio/reconcilio.cc160
-rw-r--r--src/clients/reconcilio/util/Makefile.am49
-rw-r--r--src/clients/reconcilio/util/iterator.hh93
-rw-r--r--src/clients/reconcilio/util/iterator_TEST.cc126
-rw-r--r--src/clients/reconcilio/util/realpath.cc96
-rw-r--r--src/clients/reconcilio/util/realpath.hh32
-rw-r--r--src/clients/reconcilio/util/realpath_TEST.cc68
-rwxr-xr-xsrc/clients/reconcilio/util/realpath_TEST_cleanup.sh11
-rwxr-xr-xsrc/clients/reconcilio/util/realpath_TEST_setup.sh64
-rw-r--r--src/clients/reconcilio/util/wildcard_expander.cc185
-rw-r--r--src/clients/reconcilio/util/wildcard_expander.hh60
-rw-r--r--src/clients/reconcilio/util/wildcard_expander_TEST.cc104
-rwxr-xr-xsrc/clients/reconcilio/util/wildcard_expander_TEST_cleanup.sh11
-rwxr-xr-xsrc/clients/reconcilio/util/wildcard_expander_TEST_setup.sh19
50 files changed, 4932 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 491369d..49a8257 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1002,8 +1002,8 @@ AC_DEFINE_UNQUOTED([DEFAULT_DISTRIBUTION], "$DEFAULT_DISTRIBUTION", [Default dis
dnl }}}
dnl {{{ clients
-ALL_CLIENTS="accerso adjutrix contrarius gtkpaludis inquisitio instruo paludis qualudis"
-DEFAULT_CLIENTS="adjutrix paludis"
+ALL_CLIENTS="accerso adjutrix contrarius gtkpaludis inquisitio instruo paludis qualudis reconcilio"
+DEFAULT_CLIENTS="adjutrix paludis reconcilio"
if test x"$ENABLE_QA" = "xyes" ; then
DEFAULT_CLIENTS="$DEFAULT_CLIENTS qualudis"
fi
@@ -1022,6 +1022,7 @@ AC_ARG_WITH([clients],
inquisitio A search client
instruo A metadata generation client
paludis The Paludis console client
+ reconcilio A linkage repair tool
qualudis The QA console client],
[clients="`echo $with_clients | tr ',' ' '`"],
[clients="$DEFAULT_CLIENTS"])
@@ -1306,6 +1307,10 @@ AC_OUTPUT(
src/clients/instruo/Makefile
src/clients/paludis/Makefile
src/clients/qualudis/Makefile
+ src/clients/reconcilio/Makefile
+ src/clients/reconcilio/broken_linkage_finder/Makefile
+ src/clients/reconcilio/littlelf/Makefile
+ src/clients/reconcilio/util/Makefile
src/output/Makefile
test/Makefile
vim/Makefile
diff --git a/paludis/repositories/e/ebuild/ebuild.bash b/paludis/repositories/e/ebuild/ebuild.bash
index e3c291f..45cda0b 100755
--- a/paludis/repositories/e/ebuild/ebuild.bash
+++ b/paludis/repositories/e/ebuild/ebuild.bash
@@ -222,6 +222,7 @@ ebuild_scrub_environment()
unset -v ${!GTKPALUDIS_CMDLINE_*} GTKPALUDIS_OPTIONS
unset -v ${!ADJUTRIX_CMDLINE_*} ADJUTRIX_OPTIONS
unset -v ${!QUALUDIS_CMDLINE_*} QUALUDIS_OPTIONS
+ unset -v ${!RECONCILIO_CMDLINE_*} RECONCILIO_OPTIONS
unset -v PALUDIS_HOME PALUDIS_PID EBUILD_KILL_PID ROOT
unset -v CATEGORY PN PV P PVR PF ${!LD_*}
diff --git a/src/clients/reconcilio/Makefile.am b/src/clients/reconcilio/Makefile.am
new file mode 100644
index 0000000..cb38e34
--- /dev/null
+++ b/src/clients/reconcilio/Makefile.am
@@ -0,0 +1,94 @@
+AM_CXXFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src \
+ @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_VISIBILITY@
+
+SUBDIRS = util littlelf broken_linkage_finder .
+
+bin_PROGRAMS = reconcilio
+noinst_PROGRAMS = man-reconcilio
+noinst_DATA = reconcilio.html
+man_MANS = reconcilio.1
+
+reconcilio.1 : man-reconcilio
+ ./man-reconcilio > $@
+
+reconcilio.html : man-reconcilio
+ ./man-reconcilio --html > $@
+
+man_reconcilio_SOURCES = \
+ man_reconcilio.cc \
+ command_line.hh \
+ command_line.cc
+
+man_reconcilio_LDADD = \
+ $(top_builddir)/paludis/args/libpaludisargs.la \
+ $(top_builddir)/paludis/args/libpaludisman.a \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/libpaludismanpagethings.la \
+ $(top_builddir)/src/output/liboutput.a \
+ $(DYNAMIC_LD_LIBS)
+
+reconcilio_SOURCES = \
+ command_line.hh command_line.cc \
+ fix_linkage.hh fix_linkage.cc \
+ install.hh install.cc \
+ reconcilio.cc
+
+if MONOLITHIC
+
+reconcilio_LDADD = \
+ broken_linkage_finder/libbrokenlinkagefinder.a \
+ littlelf/liblittlelf.a \
+ util/libreconcilioutil.a \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/args/libpaludisargs.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/src/output/liboutput.a \
+ $(DYNAMIC_LD_LIBS)
+
+else
+
+reconcilio_LDADD = \
+ broken_linkage_finder/libbrokenlinkagefinder.a \
+ littlelf/liblittlelf.a \
+ util/libreconcilioutil.a \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/args/libpaludisargs.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/src/output/liboutput.a \
+ $(DYNAMIC_LD_LIBS)
+
+endif
+
+TESTS_ENVIRONMENT = env \
+ TEST_SCRIPT_DIR="$(srcdir)/" \
+ PALUDIS_NO_GLOBAL_HOOKS="yes" \
+ PALUDIS_NO_XTERM_TITLES="yes" \
+ PALUDIS_OPTIONS="" \
+ PALUDIS_EBUILD_DIR="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_srcdir)/paludis/repositories/e/ebuild/`" \
+ PALUDIS_EBUILD_DIR_FALLBACK="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/repositories/e/ebuild/`" \
+ PALUDIS_EAPIS_DIR="$(top_srcdir)/paludis/repositories/e/eapis/" \
+ PALUDIS_DISTRIBUTIONS_DIR="$(top_srcdir)/paludis/distributions/" \
+ PALUDIS_DISTRIBUTION="gentoo" \
+ PALUDIS_REPOSITORY_SO_DIR="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/repositories`" \
+ PALUDIS_ENVIRONMENT_SO_DIR="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/environments`" \
+ PALUDIS_NO_CHOWN="yupyup" \
+ PALUDIS_REDUCED_USERNAME="`id -un`" \
+ PALUDIS_OUTPUTWRAPPER_DIR="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/`" \
+ TEST_OUTPUT_WRAPPER="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/outputwrapper`" \
+ SYSCONFDIR="$(sysconfdir)" \
+ bash $(top_srcdir)/test/run_test.sh bash
+
+TESTS =
+
+EXTRA_DIST = \
+ $(man_MANS) \
+ $(TESTS)
+
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda
+DISTCLEANFILES = $(man_MANS) $(noinst_DATA)
+MAINTAINERCLEANFILES = Makefile.in
+
+built-sources : $(BUILT_SOURCES)
+ for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
+
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/Makefile.am b/src/clients/reconcilio/broken_linkage_finder/Makefile.am
new file mode 100644
index 0000000..247de60
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/Makefile.am
@@ -0,0 +1,40 @@
+AM_CXXFLAGS = -I$(top_srcdir) @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_NO_WOLD_STYLE_CAST@ @PALUDIS_CXXFLAGS_NO_WSHADOW@
+
+SUBDIRS = .
+
+noinst_LIBRARIES = libbrokenlinkagefinder.a
+
+nodist_libbrokenlinkagefinder_a_SOURCES = \
+ broken_linkage_finder.cc broken_linkage_finder.hh \
+ configuration.cc configuration.hh \
+ linkage_checker.cc linkage_checker.hh \
+ libtool_linkage_checker.cc libtool_linkage_checker.hh \
+ elf_linkage_checker.cc elf_linkage_checker.hh
+
+TESTS_ENVIRONMENT = env \
+ TEST_SCRIPT_DIR="$(srcdir)/" \
+ TEST_OUTPUT_WRAPPER="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/outputwrapper`" \
+ bash $(top_srcdir)/test/run_test.sh bash
+
+TESTS = configuration_TEST
+check_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = \
+ $(TESTS) \
+ configuration_TEST_setup.sh configuration_TEST_cleanup.sh
+
+configuration_TEST_LDADD = \
+ libbrokenlinkagefinder.a \
+ ../util/libreconcilioutil.a \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/util/test_extras.o \
+ $(top_builddir)/test/libtest.a
+
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda
+MAINTAINERCLEANFILES = Makefile.in
+
+built-sources : $(BUILT_SOURCES)
+ for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
+
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.cc b/src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.cc
new file mode 100644
index 0000000..95b83e9
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.cc
@@ -0,0 +1,382 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "broken_linkage_finder.hh"
+#include "configuration.hh"
+#include "elf_linkage_checker.hh"
+#include "libtool_linkage_checker.hh"
+#include "linkage_checker.hh"
+
+#include <src/clients/reconcilio/util/iterator.hh>
+#include <src/clients/reconcilio/util/realpath.hh>
+
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/mutex.hh>
+#include <paludis/util/parallel_for_each.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/set-impl.hh>
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/util/visitor_cast.hh>
+#include <paludis/util/visitor-impl.hh>
+
+#include <paludis/contents.hh>
+#include <paludis/environment.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/package_database.hh>
+#include <paludis/package_id.hh>
+#include <paludis/query.hh>
+
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <set>
+#include <vector>
+
+using namespace paludis;
+using namespace broken_linkage_finder;
+
+typedef std::multimap<FSEntry, tr1::shared_ptr<const PackageID> > Files;
+typedef std::map<FSEntry, std::set<std::string> > PackageBreakage;
+typedef std::map<tr1::shared_ptr<const PackageID>, PackageBreakage, PackageIDSetComparator> Breakage;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<BrokenLinkageFinder>
+ {
+ const Environment * env;
+ const Configuration config;
+ std::string library;
+
+ std::vector<tr1::shared_ptr<LinkageChecker> > checkers;
+
+ Mutex mutex;
+
+ bool has_files;
+ Files files;
+
+ Breakage breakage;
+ PackageBreakage orphan_breakage;
+
+ Implementation(const Environment * the_env, const std::string & the_library) :
+ env(the_env),
+ config(the_env->root()),
+ library(the_library),
+ has_files(false)
+ {
+ }
+ };
+}
+
+namespace
+{
+ std::set<FSEntry> no_files;
+ std::set<std::string> no_reqs;
+
+ struct ParentOf : std::unary_function<FSEntry, bool>
+ {
+ const FSEntry & _child;
+
+ ParentOf(const FSEntry & child) :
+ _child(child)
+ {
+ }
+
+ bool operator() (const FSEntry & parent)
+ {
+ std::string child_str(stringify(_child)), parent_str(stringify(parent));
+ return 0 == parent_str.compare(0, parent_str.length(), child_str) &&
+ (parent_str.length() == child_str.length() || '/' == child_str[parent_str.length()]);
+ }
+ };
+}
+
+BrokenLinkageFinder::BrokenLinkageFinder(const Environment * env, const std::string & library) :
+ PrivateImplementationPattern<BrokenLinkageFinder>(new Implementation<BrokenLinkageFinder>(env, library))
+{
+ using namespace tr1::placeholders;
+
+ Context ctx("When checking for broken linkage in '" + stringify(env->root()) + "':");
+
+ _imp->checkers.push_back(tr1::shared_ptr<LinkageChecker>(new ElfLinkageChecker(library)));
+ if (library.empty())
+ _imp->checkers.push_back(tr1::shared_ptr<LinkageChecker>(new LibtoolLinkageChecker(env->root())));
+
+ std::vector<FSEntry> search_dirs_nosyms, search_dirs_pruned;
+ std::transform(_imp->config.begin_search_dirs(), _imp->config.end_search_dirs(),
+ std::back_inserter(search_dirs_nosyms),
+ tr1::bind(realpath_with_current_and_root, _1, FSEntry("/"), env->root()));
+ std::sort(search_dirs_nosyms.begin(), search_dirs_nosyms.end());
+
+ for (std::vector<FSEntry>::const_iterator it(search_dirs_nosyms.begin()),
+ it_end(search_dirs_nosyms.end()); it_end != it; ++it)
+ if (search_dirs_pruned.end() ==
+ std::find_if(search_dirs_pruned.begin(), search_dirs_pruned.end(),
+ ParentOf(*it)))
+ search_dirs_pruned.push_back(*it);
+ Log::get_instance()->message(
+ ll_debug, lc_context, "After resolving symlinks and pruning subdirectories, SEARCH_DIRS=\"" +
+ join(search_dirs_pruned.begin(), search_dirs_pruned.end(), " ") + "\"");
+
+ parallel_for_each(search_dirs_pruned.begin(), search_dirs_pruned.end(),
+ tr1::bind(&BrokenLinkageFinder::search_directory, this, _1));
+
+ tr1::function<void (const FSEntry &, const std::string &)> callback(
+ tr1::bind(&BrokenLinkageFinder::add_breakage, this, _1, _2));
+ std::for_each(_imp->checkers.begin(), _imp->checkers.end(),
+ tr1::bind(&LinkageChecker::need_breakage_added, _1, callback));
+
+ _imp->checkers.clear();
+}
+
+BrokenLinkageFinder::~BrokenLinkageFinder()
+{
+}
+
+void
+BrokenLinkageFinder::search_directory(const FSEntry & directory)
+{
+ FSEntry dir(directory);
+ do
+ {
+ dir = dir.dirname();
+ if (_imp->config.dir_is_masked(dir))
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "Skipping '" + stringify(directory) + "' because '" + stringify(dir) + "' is search-masked");
+ return;
+ }
+ }
+ while (FSEntry("/") != dir);
+
+ FSEntry with_root(_imp->env->root() / directory);
+ if (with_root.is_directory())
+ walk_directory(with_root);
+ else
+ Log::get_instance()->message(ll_debug, lc_context, "'" + stringify(directory) + "' is missing or not a directory");
+}
+
+void
+BrokenLinkageFinder::walk_directory(const FSEntry & directory)
+{
+ using namespace tr1::placeholders;
+
+ if (_imp->config.dir_is_masked(directory.strip_leading(_imp->env->root())))
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "'" + stringify(directory) + "' is search-masked");
+ return;
+ }
+
+ Log::get_instance()->message(ll_debug, lc_context, "Entering directory '" + stringify(directory) + "'");
+ try
+ {
+ parallel_for_each(DirIterator(directory, false), DirIterator(),
+ tr1::bind(&BrokenLinkageFinder::check_file, this, _1));
+ }
+ catch (const FSError & ex)
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context, ex.message());
+ }
+}
+
+void
+BrokenLinkageFinder::check_file(const FSEntry & file)
+{
+ using namespace tr1::placeholders;
+
+ try
+ {
+ if (file.is_symbolic_link())
+ {
+ FSEntry target(dereference_with_root(file, _imp->env->root()));
+ if (target.is_regular_file())
+ std::for_each(_imp->checkers.begin(), _imp->checkers.end(),
+ tr1::bind(&LinkageChecker::note_symlink, _1, file, target));
+ }
+
+ else if (file.is_directory())
+ walk_directory(file);
+
+ else if (file.is_regular_file())
+ if (_imp->checkers.end() ==
+ std::find_if(_imp->checkers.begin(), _imp->checkers.end(),
+ tr1::bind(&LinkageChecker::check_file, _1, file)))
+ Log::get_instance()->message(ll_debug, lc_context, "'" + stringify(file) + "' is not a recognised file type");
+ }
+ catch (const FSError & ex)
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context, ex.message());
+ }
+}
+
+void
+BrokenLinkageFinder::add_breakage(const FSEntry & file, const std::string & req)
+{
+ using namespace tr1::placeholders;
+
+ if (_imp->library.empty() && _imp->config.lib_is_masked(req))
+ return;
+
+ if (! _imp->has_files)
+ {
+ _imp->has_files = true;
+
+ Context ctx("When building map from files to packages:");
+
+ tr1::shared_ptr<const PackageDatabase> db(_imp->env->package_database());
+ tr1::shared_ptr<const PackageIDSequence> pkgs(
+ db->query(query::InstalledAtRoot(_imp->env->root()), qo_whatever));
+
+ parallel_for_each(pkgs->begin(), pkgs->end(),
+ tr1::bind(&BrokenLinkageFinder::gather_package, this, _1));
+ }
+
+ FSEntry without_root(file.strip_leading(_imp->env->root()));
+ std::pair<Files::const_iterator, Files::const_iterator> range(_imp->files.equal_range(without_root));
+ if (range.first == range.second)
+ _imp->orphan_breakage[without_root].insert(req);
+ else
+ while (range.first != range.second)
+ {
+ _imp->breakage[range.first->second][without_root].insert(req);
+ ++range.first;
+ }
+}
+
+void
+BrokenLinkageFinder::gather_package(const tr1::shared_ptr<const PackageID> & pkg)
+{
+ using namespace tr1::placeholders;
+
+ Context ctx("When gathering the contents of " + stringify(*pkg) + ":");
+
+ tr1::shared_ptr<const MetadataContentsKey> key(pkg->contents_key());
+ if (! key)
+ return;
+ tr1::shared_ptr<const Contents> contents(key->value());
+ if (! contents)
+ return;
+
+ for (Contents::ConstIterator it(contents->begin()),
+ it_end(contents->end()); it_end != it; ++it)
+ {
+ const ContentsFileEntry * file(visitor_cast<const ContentsFileEntry>(**it));
+ if (0 != file)
+ {
+ Lock l(_imp->mutex);
+ _imp->files.insert(std::make_pair(file->name(), pkg));
+ }
+ }
+}
+
+BrokenLinkageFinder::BrokenPackageConstIterator
+BrokenLinkageFinder::begin_broken_packages() const
+{
+ return BrokenPackageConstIterator(first_iterator(_imp->breakage.begin()));
+}
+
+BrokenLinkageFinder::BrokenPackageConstIterator
+BrokenLinkageFinder::end_broken_packages() const
+{
+ return BrokenPackageConstIterator(first_iterator(_imp->breakage.end()));
+}
+
+BrokenLinkageFinder::BrokenFileConstIterator
+BrokenLinkageFinder::begin_broken_files(const tr1::shared_ptr<const PackageID> & pkg) const
+{
+ if (pkg)
+ {
+ Breakage::const_iterator it(_imp->breakage.find(pkg));
+ if (_imp->breakage.end() == it)
+ return BrokenFileConstIterator(no_files.begin());
+
+ return BrokenFileConstIterator(first_iterator(it->second.begin()));
+ }
+ else
+ return BrokenFileConstIterator(first_iterator(_imp->orphan_breakage.begin()));
+}
+
+BrokenLinkageFinder::BrokenFileConstIterator
+BrokenLinkageFinder::end_broken_files(const tr1::shared_ptr<const PackageID> & pkg) const
+{
+ if (pkg)
+ {
+ Breakage::const_iterator it(_imp->breakage.find(pkg));
+ if (_imp->breakage.end() == it)
+ return BrokenFileConstIterator(no_files.end());
+
+ return BrokenFileConstIterator(first_iterator(it->second.end()));
+ }
+ else
+ return BrokenFileConstIterator(first_iterator(_imp->orphan_breakage.end()));
+}
+
+BrokenLinkageFinder::MissingRequirementConstIterator
+BrokenLinkageFinder::begin_missing_requirements(
+ const tr1::shared_ptr<const PackageID> & pkg, const FSEntry & file) const
+{
+ if (pkg)
+ {
+ Breakage::const_iterator pkg_it(_imp->breakage.find(pkg));
+ if (_imp->breakage.end() == pkg_it)
+ return MissingRequirementConstIterator(no_reqs.begin());
+
+ PackageBreakage::const_iterator file_it(pkg_it->second.find(file));
+ if (pkg_it->second.end() == file_it)
+ return MissingRequirementConstIterator(no_reqs.begin());
+
+ return MissingRequirementConstIterator(file_it->second.begin());
+ }
+ else
+ {
+ PackageBreakage::const_iterator file_it(_imp->orphan_breakage.find(file));
+ if (_imp->orphan_breakage.end() == file_it)
+ return MissingRequirementConstIterator(no_reqs.begin());
+
+ return MissingRequirementConstIterator(file_it->second.begin());
+ }
+}
+
+BrokenLinkageFinder::MissingRequirementConstIterator
+BrokenLinkageFinder::end_missing_requirements(
+ const tr1::shared_ptr<const PackageID> & pkg, const FSEntry & file) const
+{
+ if (pkg)
+ {
+ Breakage::const_iterator pkg_it(_imp->breakage.find(pkg));
+ if (_imp->breakage.end() == pkg_it)
+ return MissingRequirementConstIterator(no_reqs.end());
+
+ PackageBreakage::const_iterator file_it(pkg_it->second.find(file));
+ if (pkg_it->second.end() == file_it)
+ return MissingRequirementConstIterator(no_reqs.end());
+
+ return MissingRequirementConstIterator(file_it->second.end());
+ }
+ else
+ {
+ PackageBreakage::const_iterator file_it(_imp->orphan_breakage.find(file));
+ if (_imp->orphan_breakage.end() == file_it)
+ return MissingRequirementConstIterator(no_reqs.end());
+
+ return MissingRequirementConstIterator(file_it->second.end());
+ }
+}
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.hh b/src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.hh
new file mode 100644
index 0000000..5f1af22
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.hh
@@ -0,0 +1,78 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_BROKEN_LINKAGE_FINDER_HH
+#define PALUDIS_GUARD_RECONCILIO_BROKEN_LINKAGE_FINDER_HH
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/fs_entry-fwd.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/tr1_memory.hh>
+
+#include <paludis/environment-fwd.hh>
+#include <paludis/package_id-fwd.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-fwd.hh>
+
+class BrokenLinkageFinder :
+ private paludis::PrivateImplementationPattern<BrokenLinkageFinder>,
+ private paludis::InstantiationPolicy<BrokenLinkageFinder, paludis::instantiation_method::NonCopyableTag>
+{
+ public:
+ BrokenLinkageFinder(const paludis::Environment *, const std::string &);
+ ~BrokenLinkageFinder();
+
+ typedef libwrapiter::ForwardIterator<
+ BrokenLinkageFinder, const paludis::tr1::shared_ptr<const paludis::PackageID>
+ > BrokenPackageConstIterator;
+ BrokenPackageConstIterator begin_broken_packages() const PALUDIS_ATTRIBUTE((warn_unused_result));
+ BrokenPackageConstIterator end_broken_packages() const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ typedef libwrapiter::ForwardIterator<
+ BrokenLinkageFinder, const paludis::FSEntry
+ > BrokenFileConstIterator;
+ BrokenFileConstIterator begin_broken_files(const paludis::tr1::shared_ptr<const paludis::PackageID> &)
+ const PALUDIS_ATTRIBUTE((warn_unused_result));
+ BrokenFileConstIterator end_broken_files(const paludis::tr1::shared_ptr<const paludis::PackageID> &)
+ const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ typedef libwrapiter::ForwardIterator<
+ BrokenLinkageFinder, const std::string
+ > MissingRequirementConstIterator;
+ MissingRequirementConstIterator begin_missing_requirements(
+ const paludis::tr1::shared_ptr<const paludis::PackageID> &, const paludis::FSEntry &)
+ const PALUDIS_ATTRIBUTE((warn_unused_result));
+ MissingRequirementConstIterator end_missing_requirements(
+ const paludis::tr1::shared_ptr<const paludis::PackageID> &, const paludis::FSEntry &)
+ const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ private:
+ void search_directory(const paludis::FSEntry &);
+
+ void walk_directory(const paludis::FSEntry &);
+ void check_file(const paludis::FSEntry &);
+
+ void gather_package(const paludis::tr1::shared_ptr<const paludis::PackageID> &);
+
+ void add_breakage(const paludis::FSEntry &, const std::string &);
+};
+
+#endif
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/configuration.cc b/src/clients/reconcilio/broken_linkage_finder/configuration.cc
new file mode 100644
index 0000000..9c2ac52
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/configuration.cc
@@ -0,0 +1,303 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "configuration.hh"
+
+#include <src/clients/reconcilio/util/realpath.hh>
+#include <src/clients/reconcilio/util/wildcard_expander.hh>
+
+#include <paludis/util/config_file.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/options.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/system.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/tr1_functional.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-impl.hh>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <vector>
+
+using namespace paludis;
+using namespace broken_linkage_finder;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<Configuration>
+ {
+ std::vector<std::string> ld_library_mask;
+ std::vector<FSEntry> search_dirs;
+ std::vector<FSEntry> search_dirs_mask;
+ };
+}
+
+namespace
+{
+ struct IsGarbageFile : std::unary_function<const FSEntry &, bool>
+ {
+ bool operator() (const FSEntry & file)
+ {
+ std::string basename(file.basename());
+ return '#' == basename[0] || '~' == basename[basename.length() - 1];
+ }
+ };
+
+ template <typename T_, typename DelimKind_, typename DelimMode_, typename Char_>
+ void
+ from_string(const tr1::function<std::string (const std::string &)> & source,
+ const std::string & varname, std::vector<T_> & vec,
+ const Tokeniser<DelimKind_, DelimMode_, Char_> & tokeniser)
+ {
+ std::string str(source(varname));
+ if (! str.empty())
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "Got " + varname + "=\"" + str + "\"");
+ tokeniser.tokenise(str, std::back_inserter(vec));
+ }
+ }
+
+ template <typename T_>
+ inline void
+ from_string(const tr1::function<std::string (const std::string &)> & source,
+ const std::string & varname, std::vector<T_> & vec)
+ {
+ from_string(source, varname, vec, *WhitespaceTokeniser::get_instance());
+ }
+
+ inline void
+ do_wildcards(std::vector<std::string> &, const FSEntry &)
+ {
+ }
+
+ inline void
+ do_wildcards(std::vector<FSEntry> & vec, const FSEntry & root)
+ {
+ std::vector<FSEntry> scratch;
+
+ for (std::vector<FSEntry>::const_iterator it(vec.begin()), it_end(vec.end()); it_end != it; ++it)
+ std::copy(WildcardExpander(stringify(*it), root), WildcardExpander(),
+ std::back_inserter(scratch));
+
+ using std::swap;
+ swap(vec, scratch);
+ }
+
+ template <typename T_>
+ void
+ cleanup(const std::string & varname, std::vector<T_> & vec, const FSEntry & root)
+ {
+ vec.erase(std::find(vec.begin(), vec.end(), T_("-*")), vec.end());
+
+ do_wildcards(vec, root);
+
+ std::sort(vec.begin(), vec.end());
+ vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
+
+ Log::get_instance()->message(
+ ll_debug, lc_context, "Final " + varname + "=\"" +
+ join(vec.begin(), vec.end(), " ") + "\"");
+ }
+}
+
+Configuration::Configuration(const FSEntry & root) :
+ PrivateImplementationPattern<Configuration>(new Implementation<Configuration>)
+{
+ Context ctx("When loading broken linkage checker configuration for '" + stringify(root) + "':");
+
+ load_from_environment();
+ load_from_etc_revdep_rebuild(root);
+ load_from_etc_profile_env(root);
+ load_from_etc_ld_so_conf(root);
+ add_defaults();
+
+ cleanup("LD_LIBRARY_MASK", _imp->ld_library_mask, root);
+ cleanup("SEARCH_DIRS", _imp->search_dirs, root);
+ cleanup("SEARCH_DIRS_MASK", _imp->search_dirs_mask, root);
+}
+
+Configuration::~Configuration()
+{
+}
+
+void
+Configuration::load_from_environment()
+{
+ using namespace tr1::placeholders;
+
+ Context ctx("When checking environment variables:");
+
+ tr1::function<std::string (const std::string &)> fromenv(
+ tr1::bind(getenv_with_default, _1, ""));
+
+ from_string(fromenv, "LD_LIBRARY_MASK", _imp->ld_library_mask);
+ from_string(fromenv, "SEARCH_DIRS", _imp->search_dirs);
+ from_string(fromenv, "SEARCH_DIRS_MASK", _imp->search_dirs_mask);
+}
+
+void
+Configuration::load_from_etc_revdep_rebuild(const FSEntry & root)
+{
+ using namespace tr1::placeholders;
+
+ FSEntry etc_revdep_rebuild(root / "etc" / "revdep-rebuild");
+ Context ctx("When reading '" + stringify(etc_revdep_rebuild) + "':");
+
+ if (etc_revdep_rebuild.is_directory_or_symlink_to_directory())
+ {
+ std::vector<FSEntry> conf_files = std::vector<FSEntry>(
+ DirIterator(etc_revdep_rebuild), DirIterator());
+ conf_files.erase(std::remove_if(conf_files.begin(), conf_files.end(),
+ IsGarbageFile()),
+ conf_files.end());
+ std::sort(conf_files.begin(), conf_files.end());
+
+ KeyValueConfigFileOptions opts;
+ opts += kvcfo_disallow_space_around_equals;
+ opts += kvcfo_disallow_space_inside_unquoted_values;
+
+ for (std::vector<FSEntry>::iterator it(conf_files.begin()),
+ it_end(conf_files.end()); it_end != it; ++it)
+ {
+ Context ctx_file("When reading '" + stringify(*it) + "':");
+
+ if (it->is_regular_file_or_symlink_to_regular_file())
+ {
+ KeyValueConfigFile kvs(*it, opts);
+
+ tr1::function<std::string (const std::string &)> fromfile(
+ tr1::bind(&KeyValueConfigFile::get, tr1::cref(kvs), _1));
+
+ from_string(fromfile, "LD_LIBRARY_MASK", _imp->ld_library_mask);
+ from_string(fromfile, "SEARCH_DIRS", _imp->search_dirs);
+ from_string(fromfile, "SEARCH_DIRS_MASK", _imp->search_dirs_mask);
+ }
+ else
+ Log::get_instance()->message(ll_warning, lc_context, "'" + stringify(*it) + "' is not a regular file");
+ }
+ }
+ else if (etc_revdep_rebuild.exists())
+ Log::get_instance()->message(ll_warning, lc_context, "'" + stringify(etc_revdep_rebuild) + "' exists but is not a directory");
+}
+
+void
+Configuration::load_from_etc_profile_env(const FSEntry & root)
+{
+ using namespace tr1::placeholders;
+
+ FSEntry etc_profile_env(root / "etc" / "profile.env");
+ Context ctx("When reading '" + stringify(etc_profile_env) + "':");
+
+ if (etc_profile_env.is_regular_file_or_symlink_to_regular_file())
+ {
+ KeyValueConfigFileOptions opts;
+ opts += kvcfo_disallow_space_around_equals;
+ opts += kvcfo_disallow_space_inside_unquoted_values;
+ opts += kvcfo_ignore_export;
+
+ KeyValueConfigFile kvs(etc_profile_env, opts);
+ Tokeniser<delim_kind::AnyOfTag, delim_mode::DelimiterTag> tokeniser(":");
+
+ tr1::function<std::string (const std::string &)> fromfile(
+ tr1::bind(&KeyValueConfigFile::get, tr1::cref(kvs), _1));
+
+ from_string(fromfile, "PATH", _imp->search_dirs, tokeniser);
+ from_string(fromfile, "ROOTPATH", _imp->search_dirs, tokeniser);
+ }
+ else if (etc_profile_env.exists())
+ Log::get_instance()->message(ll_warning, lc_context, "'" + stringify(etc_profile_env) + "' exists but is not a regular file");
+}
+
+void
+Configuration::load_from_etc_ld_so_conf(const FSEntry & root)
+{
+ FSEntry etc_ld_so_conf(root / "etc" / "ld.so.conf");
+ Context ctx("When reading '" + stringify(etc_ld_so_conf) + "':");
+
+ if (etc_ld_so_conf.is_regular_file_or_symlink_to_regular_file())
+ {
+ LineConfigFileOptions opts;
+ opts += lcfo_disallow_continuations;
+
+ LineConfigFile lines(etc_ld_so_conf, opts);
+ if (lines.begin() != lines.end())
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "Got " + join(lines.begin(), lines.end(), " "));
+ std::copy(lines.begin(), lines.end(), std::back_inserter(_imp->search_dirs));
+ }
+ }
+ else if (etc_ld_so_conf.exists())
+ Log::get_instance()->message(ll_warning, lc_context, "'" + stringify(etc_ld_so_conf) + "' exists but is not a regular file");
+}
+
+void
+Configuration::add_defaults()
+{
+ Context ctx("When adding default settings:");
+
+ static const std::string ld_library_mask(
+ "libodbcinst.so libodbc.so libjava.so libjvm.so");
+ static const std::string search_dirs(
+ "/bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*");
+ static const std::string search_dirs_mask(
+ "/opt/OpenOffice /usr/lib*/openoffice /lib*/modules");
+
+ Log::get_instance()->message(ll_debug, lc_context, "Got LD_LIBRARY_MASK=\"" + ld_library_mask + "\"");
+ WhitespaceTokeniser::get_instance()->tokenise(
+ ld_library_mask, std::back_inserter(_imp->ld_library_mask));
+
+ Log::get_instance()->message(ll_debug, lc_context, "Got SEARCH_DIRS=\"" + search_dirs + "\"");
+ WhitespaceTokeniser::get_instance()->tokenise(
+ search_dirs, std::back_inserter(_imp->search_dirs));
+
+ Log::get_instance()->message(ll_debug, lc_context, "Got SEARCH_DIRS_MASK=\"" + search_dirs_mask + "\"");
+ WhitespaceTokeniser::get_instance()->tokenise(
+ search_dirs_mask, std::back_inserter(_imp->search_dirs_mask));
+}
+
+Configuration::DirsIterator
+Configuration::begin_search_dirs() const
+{
+ return DirsIterator(_imp->search_dirs.begin());
+}
+
+Configuration::DirsIterator
+Configuration::end_search_dirs() const
+{
+ return DirsIterator(_imp->search_dirs.end());
+}
+
+bool
+Configuration::dir_is_masked(const FSEntry & dir) const
+{
+ return std::binary_search(_imp->search_dirs_mask.begin(), _imp->search_dirs_mask.end(), dir);
+}
+
+bool
+Configuration::lib_is_masked(const std::string & lib) const
+{
+ return std::binary_search(_imp->ld_library_mask.begin(), _imp->ld_library_mask.end(), lib);
+}
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/configuration.hh b/src/clients/reconcilio/broken_linkage_finder/configuration.hh
new file mode 100644
index 0000000..2039f03
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/configuration.hh
@@ -0,0 +1,59 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_BROKEN_LINKAGE_FINDER_CONFIGURATION_HH
+#define PALUDIS_GUARD_RECONCILIO_BROKEN_LINKAGE_FINDER_CONFIGURATION_HH
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/fs_entry-fwd.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-fwd.hh>
+
+#include <string>
+
+namespace broken_linkage_finder
+{
+ class Configuration :
+ private paludis::PrivateImplementationPattern<Configuration>,
+ private paludis::InstantiationPolicy<Configuration, paludis::instantiation_method::NonCopyableTag>
+ {
+ public:
+ Configuration(const paludis::FSEntry &);
+ ~Configuration();
+
+ typedef libwrapiter::ForwardIterator<Configuration, const paludis::FSEntry> DirsIterator;
+ DirsIterator begin_search_dirs() const PALUDIS_ATTRIBUTE((warn_unused_result));
+ DirsIterator end_search_dirs() const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ bool dir_is_masked(const paludis::FSEntry &) const PALUDIS_ATTRIBUTE((warn_unused_result));
+ bool lib_is_masked(const std::string &) const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ private:
+ void load_from_environment();
+ void load_from_etc_revdep_rebuild(const paludis::FSEntry &);
+ void load_from_etc_profile_env(const paludis::FSEntry &);
+ void load_from_etc_ld_so_conf(const paludis::FSEntry &);
+ void add_defaults();
+ };
+}
+
+#endif
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/configuration_TEST.cc b/src/clients/reconcilio/broken_linkage_finder/configuration_TEST.cc
new file mode 100644
index 0000000..7d9e241
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/configuration_TEST.cc
@@ -0,0 +1,71 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "configuration.hh"
+
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/join.hh>
+
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-impl.hh>
+
+#include <unistd.h>
+
+using namespace broken_linkage_finder;
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct ConfigurationTest : TestCase
+ {
+ ConfigurationTest() : TestCase("configuration") {}
+
+ void run()
+ {
+ setenv("SEARCH_DIRS", "/quuxlib", 1);
+ setenv("SEARCH_DIRS_MASK", "/quuxlib/quux", 1);
+ setenv("LD_LIBRARY_MASK", "libquux.so", 1);
+
+ Configuration config(FSEntry::cwd() / "configuration_TEST_dir");
+
+ TEST_CHECK_EQUAL(join(config.begin_search_dirs(), config.end_search_dirs(), " "),
+ "/alib /barbin /barlib/foo /bazbin /bin /blib /foobin /foolib/bar /lib32 /lib64 /quuxlib /sbin /usr/bin /usr/lib* /usr/sbin");
+
+ TEST_CHECK(config.dir_is_masked(FSEntry("/meh")));
+ TEST_CHECK(config.dir_is_masked(FSEntry("/quuxlib/quux")));
+ TEST_CHECK(! config.dir_is_masked(FSEntry("/feh")));
+ TEST_CHECK(! config.dir_is_masked(FSEntry("/opt/OpenOffice")));
+ TEST_CHECK(! config.dir_is_masked(FSEntry("/usr/lib/openoffice")));
+ TEST_CHECK(! config.dir_is_masked(FSEntry("/foo")));
+
+ TEST_CHECK(config.lib_is_masked("libquux.so"));
+ TEST_CHECK(config.lib_is_masked("libxyzzy.so"));
+ TEST_CHECK(config.lib_is_masked("libodbcinst.so"));
+ TEST_CHECK(config.lib_is_masked("libodbc.so"));
+ TEST_CHECK(config.lib_is_masked("libjava.so"));
+ TEST_CHECK(config.lib_is_masked("libjvm.so"));
+ TEST_CHECK(! config.lib_is_masked("libfoo.so"));
+ }
+ } configuration_test;
+}
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/configuration_TEST_cleanup.sh b/src/clients/reconcilio/broken_linkage_finder/configuration_TEST_cleanup.sh
new file mode 100755
index 0000000..34e752d
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/configuration_TEST_cleanup.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d configuration_TEST_dir ] ; then
+ rm -fr configuration_TEST_dir
+else
+ true
+fi
+
+
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/configuration_TEST_setup.sh b/src/clients/reconcilio/broken_linkage_finder/configuration_TEST_setup.sh
new file mode 100755
index 0000000..af01f32
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/configuration_TEST_setup.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir configuration_TEST_dir || exit 1
+cd configuration_TEST_dir || exit 1
+
+mkdir -p etc/revdep-rebuild
+
+echo 'export PATH="/foobin:/barbin"' >>etc/profile.env
+echo 'export ROOTPATH="/bazbin"' >>etc/profile.env
+echo 'export WRATH="/goo"' >>etc/profile.env
+
+echo '/foolib/bar' >>etc/ld.so.conf
+echo '/barlib/foo' >>etc/ld.so.conf
+echo '#/shoe' >>etc/ld.so.conf
+
+echo 'SEARCH_DIRS="/alib /blib"' >>etc/revdep-rebuild/10-test
+echo 'SEARCH_DIRS_MASK="/meh -*"' >>etc/revdep-rebuild/10-test
+echo 'SEARCH_DIRS_MASK="/feh"' >>etc/revdep-rebuild/20-test
+echo 'LD_LIBRARY_MASK="libxyzzy.so"' >>etc/revdep-rebuild/30-test
+
+echo 'SEARCH_DIRS="/moo"' >>etc/revdep-rebuild/10test~
+echo 'SEARCH_DIRS="/foo"' >>etc/revdep-rebuild/.10test
+echo 'SEARCH_DIRS="/boo"' >>etc/revdep-rebuild/\#10test
+
+mkdir lib32 lib64
+
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.cc b/src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.cc
new file mode 100644
index 0000000..6794177
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.cc
@@ -0,0 +1,270 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "elf_linkage_checker.hh"
+
+#include <src/clients/reconcilio/util/iterator.hh>
+
+#include <src/clients/reconcilio/littlelf/Elf.hh>
+#include <src/clients/reconcilio/littlelf/ElfDynamicSection.hh>
+#include <src/clients/reconcilio/littlelf/ElfTypes.hh>
+
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/iterator.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/mutex.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/set.hh>
+#include <paludis/util/visitor_cast.hh>
+#include <paludis/util/visitor-impl.hh>
+
+#include <algorithm>
+#include <cerrno>
+#include <fstream>
+#include <map>
+#include <set>
+#include <vector>
+
+#define tr1 paludis::tr1 // XXX
+
+using namespace paludis;
+using namespace broken_linkage_finder;
+
+namespace broken_linkage_finder
+{
+ struct ElfArchitecture
+ {
+ // not in elf.h; from glibc-2.5/sysdeps/s390/s390-32/dl-machine.h
+ static const unsigned EM_S390_OLD = 0xA390;
+
+ unsigned _machine;
+ unsigned char _class, _os_abi, _os_abi_version;
+ bool _bigendian, _mips_n32;
+
+ bool operator< (const ElfArchitecture & other) const
+ {
+ if (_machine != other._machine)
+ return _machine < other._machine;
+ if (_class != other._class)
+ return _class < other._class;
+ if (_os_abi != other._os_abi)
+ return _os_abi < other._os_abi;
+ if (_os_abi_version != other._os_abi_version)
+ return _os_abi_version < other._os_abi_version;
+ if (_bigendian != other._bigendian)
+ return _bigendian < other._bigendian;
+ return _mips_n32 < other._mips_n32;
+ }
+
+ static unsigned normalise_arch(unsigned arch)
+ {
+ switch (arch)
+ {
+ case EM_MIPS_RS3_LE:
+ return EM_MIPS;
+ case EM_S390_OLD:
+ return EM_S390;
+ }
+ return arch;
+ }
+
+ template <typename ElfType_>
+ ElfArchitecture(const ElfObject<ElfType_> & elf) :
+ _machine(normalise_arch(elf.get_arch())),
+ _class(ElfType_::elf_class),
+ _os_abi(elf.get_os_abi()),
+ _os_abi_version(elf.get_os_abi_version()),
+ _bigendian(elf.is_big_endian()),
+ _mips_n32(EM_MIPS == _machine && EF_MIPS_ABI2 & elf.get_flags())
+ {
+ }
+ };
+}
+
+typedef std::multimap<FSEntry, FSEntry> Symlinks;
+typedef std::map<ElfArchitecture, std::map<std::string, std::vector<FSEntry> > > Needed;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<ElfLinkageChecker>
+ {
+ std::string library;
+
+ Mutex mutex;
+
+ std::map<FSEntry, ElfArchitecture> seen;
+ Symlinks symlinks;
+
+ std::map<ElfArchitecture, std::vector<std::string> > libraries;
+ Needed needed;
+
+ Implementation(const std::string & the_library) :
+ library(the_library)
+ {
+ }
+ };
+}
+
+ElfLinkageChecker::ElfLinkageChecker(const std::string & library) :
+ PrivateImplementationPattern<ElfLinkageChecker>(new Implementation<ElfLinkageChecker>(library))
+{
+}
+
+ElfLinkageChecker::~ElfLinkageChecker()
+{
+}
+
+bool
+ElfLinkageChecker::check_file(const FSEntry & file)
+{
+ std::string basename(file.basename());
+ if (! (std::string::npos != basename.find(".so.") ||
+ (3 <= basename.length() && ".so" == basename.substr(basename.length() - 3)) ||
+ file.has_permission(fs_ug_owner, fs_perm_execute)))
+ return false;
+
+ std::ifstream stream(stringify(file).c_str());
+ if (! stream)
+ throw FSError("Error opening file '" + stringify(file) + "': " + strerror(errno));
+
+ return check_elf<Elf32Type>(file, stream) || check_elf<Elf64Type>(file, stream);
+}
+
+template <typename ElfType_>
+bool
+ElfLinkageChecker::check_elf(const FSEntry & file, std::ifstream & stream)
+{
+ if (! ElfObject<ElfType_>::is_valid_elf(stream))
+ return false;
+
+ try
+ {
+ Context ctx("When checking '" + stringify(file) + "' as a " +
+ stringify<int>(ElfType_::elf_class * 32) + "-bit ELF file:");
+ ElfObject<ElfType_> elf(stream);
+ if (ET_EXEC != elf.get_type() && ET_DYN != elf.get_type())
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "File is not an executable or shared library");
+ return true;
+ }
+
+ ElfArchitecture arch(elf);
+ elf.resolve_all_strings();
+
+ Lock l(_imp->mutex);
+
+ if (_imp->library.empty() && ET_DYN == elf.get_type())
+ handle_library(file, arch);
+
+ for (typename ElfObject<ElfType_>::SectionIterator sec_it(elf.section_begin()),
+ sec_it_end(elf.section_end()); sec_it_end != sec_it; ++sec_it)
+ {
+ const DynamicSection<ElfType_> * dyn_sec(visitor_cast<const DynamicSection<ElfType_> >(*sec_it));
+
+ if (0 != dyn_sec)
+ for (typename DynamicSection<ElfType_>::EntryIterator ent_it(dyn_sec->entry_begin()),
+ ent_it_end(dyn_sec->entry_end()); ent_it_end != ent_it; ++ent_it)
+ {
+ const DynamicEntryString<ElfType_> * ent_str(visitor_cast<const DynamicEntryString<ElfType_> >(*ent_it));
+
+ if (0 != ent_str && "NEEDED" == ent_str->tag_name())
+ {
+ const std::string & req((*ent_str)());
+ if (_imp->library.empty() || _imp->library == req)
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "File depends on " + req);
+ _imp->needed[arch][req].push_back(file);
+ }
+ }
+ }
+ }
+ }
+ catch (const InvalidElfFileError &)
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context, "'" + stringify(file) + "' appears to be invalid or corrupted");
+ }
+
+ return true;
+}
+
+void
+ElfLinkageChecker::handle_library(const FSEntry & file, const ElfArchitecture & arch)
+{
+ _imp->seen.insert(std::make_pair(file, arch));
+ std::pair<Symlinks::const_iterator, Symlinks::const_iterator> range(_imp->symlinks.equal_range(file));
+ _imp->libraries[arch].push_back(file.basename());
+
+ if (range.first != range.second)
+ {
+ Log::get_instance()->message(
+ ll_debug, lc_context, "Known symlinks are " +
+ join(second_iterator(range.first), second_iterator(range.second), " "));
+ std::transform(second_iterator(range.first), second_iterator(range.second),
+ std::back_inserter(_imp->libraries[arch]), tr1::mem_fn(&FSEntry::basename));
+ }
+}
+
+void
+ElfLinkageChecker::note_symlink(const FSEntry & link, const FSEntry & target)
+{
+ if (_imp->library.empty())
+ {
+ Lock l(_imp->mutex);
+
+ std::map<FSEntry, ElfArchitecture>::const_iterator it(_imp->seen.find(target));
+ if (_imp->seen.end() != it)
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "'" + stringify(link) + "' is a symlink to known library '" + stringify(target) + "'");
+ _imp->libraries[it->second].push_back(link.basename());
+ }
+ else
+ _imp->symlinks.insert(std::make_pair(target, link));
+ }
+}
+
+void
+ElfLinkageChecker::need_breakage_added(
+ const tr1::function<void (const FSEntry &, const std::string &)> & callback)
+{
+ for (Needed::iterator arch_it(_imp->needed.begin()),
+ arch_it_end(_imp->needed.end()); arch_it_end != arch_it; ++arch_it)
+ {
+ std::sort(_imp->libraries[arch_it->first].begin(), _imp->libraries[arch_it->first].end());
+ _imp->libraries[arch_it->first].erase(
+ std::unique(_imp->libraries[arch_it->first].begin(),
+ _imp->libraries[arch_it->first].end()),
+ _imp->libraries[arch_it->first].end());
+
+ std::vector<std::string> missing;
+ std::set_difference(first_iterator(arch_it->second.begin()),
+ first_iterator(arch_it->second.end()),
+ _imp->libraries[arch_it->first].begin(),
+ _imp->libraries[arch_it->first].end(),
+ std::back_inserter(missing));
+
+ for (std::vector<std::string>::const_iterator req_it(missing.begin()),
+ req_it_end(missing.end()); req_it_end != req_it; ++req_it)
+ for (std::vector<FSEntry>::const_iterator file_it(arch_it->second[*req_it].begin()),
+ file_it_end(arch_it->second[*req_it].end()); file_it_end != file_it; ++file_it)
+ callback(*file_it, *req_it);
+ }
+}
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.hh b/src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.hh
new file mode 100644
index 0000000..ba79cc2
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/elf_linkage_checker.hh
@@ -0,0 +1,55 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_BROKEN_LINKAGE_FINDER_ELF_LINKAGE_CHECKER_HH
+#define PALUDIS_GUARD_RECONCILIO_BROKEN_LINKAGE_FINDER_ELF_LINKAGE_CHECKER_HH
+
+#include "linkage_checker.hh"
+
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/tr1_functional.hh>
+
+#include <iosfwd>
+
+namespace broken_linkage_finder
+{
+ class ElfArchitecture;
+
+ class ElfLinkageChecker :
+ public LinkageChecker,
+ private paludis::PrivateImplementationPattern<ElfLinkageChecker>
+ {
+ public:
+ ElfLinkageChecker(const std::string &);
+ virtual ~ElfLinkageChecker();
+
+ virtual bool check_file(const paludis::FSEntry &) PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual void note_symlink(const paludis::FSEntry &, const paludis::FSEntry &);
+
+ virtual void need_breakage_added(
+ const paludis::tr1::function<void (const paludis::FSEntry &, const std::string &)> &);
+
+ private:
+ template <typename> bool check_elf(const paludis::FSEntry &, std::ifstream &);
+ void handle_library(const paludis::FSEntry &, const ElfArchitecture &);
+ };
+}
+
+#endif
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.cc b/src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.cc
new file mode 100644
index 0000000..0d52050
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.cc
@@ -0,0 +1,163 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "libtool_linkage_checker.hh"
+
+#include <src/clients/reconcilio/util/realpath.hh>
+
+#include <paludis/util/config_file.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/mutex.hh>
+#include <paludis/util/options.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/tokeniser.hh>
+
+#include <algorithm>
+#include <cerrno>
+#include <fstream>
+#include <functional>
+#include <vector>
+
+using namespace paludis;
+using namespace broken_linkage_finder;
+
+typedef std::vector<std::pair<FSEntry, std::string> > Breakage;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<LibtoolLinkageChecker>
+ {
+ FSEntry root;
+
+ Mutex mutex;
+
+ Breakage breakage;
+
+ Implementation(const FSEntry & the_root) :
+ root(the_root)
+ {
+ }
+ };
+}
+
+namespace
+{
+ struct IsNotAbsolutePath : std::unary_function<std::string, bool>
+ {
+ bool operator() (const std::string & str)
+ {
+ return str.empty() || '/' != str[0];
+ }
+ };
+}
+
+LibtoolLinkageChecker::LibtoolLinkageChecker(const FSEntry & root) :
+ PrivateImplementationPattern<LibtoolLinkageChecker>(new Implementation<LibtoolLinkageChecker>(root))
+{
+}
+
+LibtoolLinkageChecker::~LibtoolLinkageChecker()
+{
+}
+
+bool
+LibtoolLinkageChecker::check_file(const FSEntry & file)
+{
+ std::string basename(file.basename());
+ if (! (3 <= basename.length() &&
+ ".la" == basename.substr(basename.length() - 3)))
+ return false;
+
+ Context ctx("When checking '" + stringify(file) + "' as a libtool library:");
+
+ std::ifstream stream(stringify(file).c_str());
+ if (! stream)
+ throw FSError("Error opening file '" + stringify(file) + "': " + strerror(errno));
+
+ KeyValueConfigFileOptions opts;
+ opts += kvcfo_disallow_space_around_equals;
+ opts += kvcfo_disallow_space_inside_unquoted_values;
+
+ std::vector<std::string> deps;
+
+ try
+ {
+ KeyValueConfigFile kvs(stream, opts);
+ WhitespaceTokeniser::get_instance()->tokenise(
+ kvs.get("dependency_libs"), std::back_inserter(deps));
+ }
+ catch (const ConfigFileError & ex)
+ {
+ Log::get_instance()->message(ll_warning, lc_context, ex.message());
+ return true;
+ }
+
+ deps.erase(std::remove_if(deps.begin(), deps.end(), IsNotAbsolutePath()), deps.end());
+ if (deps.empty())
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "No libtool library dependencies found");
+ return true;
+ }
+
+ for (std::vector<std::string>::const_iterator it(deps.begin()),
+ it_end(deps.end()); it_end != it; ++it)
+ {
+ try
+ {
+ FSEntry dep(_imp->root / *it);
+ if (! dereference_with_root(dep, _imp->root).is_regular_file())
+ {
+ Log::get_instance()->message(
+ ll_debug, lc_context, "Dependency '" + *it +
+ "' is missing or not a regular file in '" + stringify(_imp->root) + "'");
+
+ Lock l(_imp->mutex);
+ _imp->breakage.push_back(std::make_pair(file, *it));
+ }
+
+ else
+ Log::get_instance()->message(ll_debug, lc_context, "Dependency '" + *it +
+ "' exists in '" + stringify(_imp->root) + "'");
+ }
+
+ catch (const FSError & ex)
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context, ex.message());
+ }
+ }
+
+ return true;
+}
+
+void
+LibtoolLinkageChecker::note_symlink(const FSEntry &, const FSEntry &)
+{
+}
+
+void
+LibtoolLinkageChecker::need_breakage_added(
+ const tr1::function<void (const FSEntry &, const std::string &)> & callback)
+{
+ for (Breakage::const_iterator it(_imp->breakage.begin()), it_end(_imp->breakage.end()); it_end != it; ++it)
+ callback(it->first, it->second);
+}
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.hh b/src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.hh
new file mode 100644
index 0000000..aa370ef
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/libtool_linkage_checker.hh
@@ -0,0 +1,46 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_BROKEN_LINKAGE_FINDER_LIBTOOL_LINKAGE_CHECKER_HH
+#define PALUDIS_GUARD_RECONCILIO_BROKEN_LINKAGE_FINDER_LIBTOOL_LINKAGE_CHECKER_HH
+
+#include "linkage_checker.hh"
+
+#include <paludis/util/private_implementation_pattern.hh>
+
+namespace broken_linkage_finder
+{
+ class LibtoolLinkageChecker :
+ public LinkageChecker,
+ private paludis::PrivateImplementationPattern<LibtoolLinkageChecker>
+ {
+ public:
+ LibtoolLinkageChecker(const paludis::FSEntry &);
+ virtual ~LibtoolLinkageChecker();
+
+ virtual bool check_file(const paludis::FSEntry &) PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual void note_symlink(const paludis::FSEntry &, const paludis::FSEntry &);
+
+ virtual void need_breakage_added(
+ const paludis::tr1::function<void (const paludis::FSEntry &, const std::string &)> &);
+ };
+}
+
+#endif
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/linkage_checker.cc b/src/clients/reconcilio/broken_linkage_finder/linkage_checker.cc
new file mode 100644
index 0000000..496f702
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/linkage_checker.cc
@@ -0,0 +1,31 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "linkage_checker.hh"
+
+using namespace broken_linkage_finder;
+
+LinkageChecker::LinkageChecker()
+{
+}
+
+LinkageChecker::~LinkageChecker()
+{
+}
+
diff --git a/src/clients/reconcilio/broken_linkage_finder/linkage_checker.hh b/src/clients/reconcilio/broken_linkage_finder/linkage_checker.hh
new file mode 100644
index 0000000..3e30f6e
--- /dev/null
+++ b/src/clients/reconcilio/broken_linkage_finder/linkage_checker.hh
@@ -0,0 +1,54 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_BROKEN_LINKAGE_FINDER_LINKAGE_CHECKER_HH
+#define PALUDIS_GUARD_RECONCILIO_BROKEN_LINKAGE_FINDER_LINKAGE_CHECKER_HH
+
+#include "broken_linkage_finder.hh"
+
+#include <paludis/util/fs_entry-fwd.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/set-fwd.hh>
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/util/tr1_memory.hh>
+
+#include <paludis/package_id-fwd.hh>
+
+#include <string>
+
+namespace broken_linkage_finder
+{
+ class LinkageChecker :
+ private paludis::InstantiationPolicy<LinkageChecker, paludis::instantiation_method::NonCopyableTag>
+ {
+ public:
+ LinkageChecker();
+ virtual ~LinkageChecker();
+
+ virtual bool check_file(const paludis::FSEntry &) PALUDIS_ATTRIBUTE((warn_unused_result)) = 0;
+ virtual void note_symlink(const paludis::FSEntry &, const paludis::FSEntry &) = 0;
+
+ virtual void need_breakage_added(
+ const paludis::tr1::function<void (const paludis::FSEntry &, const std::string &)> &) = 0;
+
+ };
+}
+
+#endif
+
diff --git a/src/clients/reconcilio/command_line.cc b/src/clients/reconcilio/command_line.cc
new file mode 100644
index 0000000..1b6b6b6
--- /dev/null
+++ b/src/clients/reconcilio/command_line.cc
@@ -0,0 +1,97 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "command_line.hh"
+
+#include <paludis/util/instantiation_policy-impl.hh>
+
+using namespace paludis;
+
+template class InstantiationPolicy<CommandLine, instantiation_method::SingletonTag>;
+
+CommandLine::CommandLine() :
+ ArgsHandler(),
+
+ action_args(this, "Actions",
+ "Selects which basic action to perform. At most one action should "
+ "be specified."),
+ a_fix_linkage(&action_args, "fix-linkage", '\0', "Search for and rebuild packages linked against non-existant libraries (default)"),
+ a_version(&action_args, "version", 'V', "Display program version"),
+ a_help(&action_args, "help", 'h', "Display program help"),
+
+ general_args(this, "General options",
+ "Options which are relvevant for most or all actions."),
+ a_log_level(&general_args, "log-level", '\0'),
+ a_no_colour(&general_args, "no-colour", '\0', "Do not use colour"),
+ a_no_color(&a_no_colour, "no-color"),
+ a_environment(&general_args, "environment", 'E', "Environment specification (class:suffix, both parts optional)"),
+ a_exact(&general_args, "exact", '\0', "Rebuild the same package version that is currently installed"),
+ a_verbose(&general_args, "verbose", 'v', "Display more detailed output"),
+ a_resume_command_template(&general_args, "resume-command-template", '\0', "Save the resume command to a file. If the filename contains 'XXXXXX', use mkstemp(3) to generate the filename"),
+
+ fix_linkage_args(this, "Fix Linkage options",
+ "Options which are relevant for --fix-linkage."),
+ a_library(&fix_linkage_args, "library", '\0', "Only rebuild packages linked against this library, even if it exists"),
+
+ install_args(this, "Install options",
+ "Options which are relevant for the install process."),
+ dl_args(this)
+{
+ add_usage_line("[ --fix-linkage ] [fix linkage options]");
+ add_usage_line("--help");
+
+ // XXX destinations support
+ install_args.a_destinations.remove();
+ install_args.a_preserve_world.remove();
+ install_args.a_preserve_world.set_specified(true);
+ install_args.a_add_to_world_spec.remove();
+
+ dl_args.dl_reinstall_targets.remove();
+ dl_args.dl_upgrade.set_default_arg("as-needed");
+ dl_args.dl_new_slots.set_default_arg("as-needed");
+
+ add_environment_variable("RECONCILIO_OPTIONS", "Default command-line options.");
+}
+
+std::string
+CommandLine::app_name() const
+{
+ return "reconcilio";
+}
+
+std::string
+CommandLine::app_synopsis() const
+{
+ return "A broken package rebuilder for Paludis, the other package mangler";
+}
+
+std::string
+CommandLine::app_description() const
+{
+ return
+ "reconcilio searches for and rebuilds packages that are linked against "
+ "libraries that are not present on the system, or a specific library "
+ "named by the user.";
+}
+
+CommandLine::~CommandLine()
+{
+}
+
+
diff --git a/src/clients/reconcilio/command_line.hh b/src/clients/reconcilio/command_line.hh
new file mode 100644
index 0000000..26fe4b1
--- /dev/null
+++ b/src/clients/reconcilio/command_line.hh
@@ -0,0 +1,68 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_COMMAND_LINE_HH
+#define PALUDIS_GUARD_RECONCILIO_COMMAND_LINE_HH
+
+#include <paludis/util/instantiation_policy.hh>
+
+#include <paludis/args/args.hh>
+#include <paludis/args/dep_list_args_group.hh>
+#include <paludis/args/install_args_group.hh>
+#include <paludis/args/log_level_arg.hh>
+
+class CommandLine :
+ public paludis::args::ArgsHandler,
+ public paludis::InstantiationPolicy<CommandLine, paludis::instantiation_method::SingletonTag>
+{
+ friend class paludis::InstantiationPolicy<CommandLine, paludis::instantiation_method::SingletonTag>;
+
+ private:
+ CommandLine();
+ ~CommandLine();
+
+ public:
+ virtual std::string app_name() const;
+ virtual std::string app_synopsis() const;
+ virtual std::string app_description() const;
+
+ paludis::args::ArgsGroup action_args;
+ paludis::args::SwitchArg a_fix_linkage;
+ paludis::args::SwitchArg a_version;
+ paludis::args::SwitchArg a_help;
+
+ paludis::args::ArgsGroup general_args;
+ paludis::args::LogLevelArg a_log_level;
+ paludis::args::SwitchArg a_no_colour;
+ paludis::args::AliasArg a_no_color;
+ paludis::args::StringArg a_environment;
+ paludis::args::SwitchArg a_exact;
+ paludis::args::SwitchArg a_verbose;
+ paludis::args::StringArg a_resume_command_template;
+
+ paludis::args::ArgsGroup fix_linkage_args;
+ paludis::args::StringArg a_library;
+
+ paludis::args::InstallArgsGroup install_args;
+ paludis::args::DepListArgsGroup dl_args;
+};
+
+#endif
+
+
diff --git a/src/clients/reconcilio/fix_linkage.cc b/src/clients/reconcilio/fix_linkage.cc
new file mode 100644
index 0000000..ae6936a
--- /dev/null
+++ b/src/clients/reconcilio/fix_linkage.cc
@@ -0,0 +1,143 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "command_line.hh"
+#include "fix_linkage.hh"
+#include "install.hh"
+
+#include <src/clients/reconcilio/broken_linkage_finder/broken_linkage_finder.hh>
+
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/stringify.hh>
+
+#include <paludis/dep_spec.hh>
+#include <paludis/name.hh>
+#include <paludis/package_id.hh>
+#include <paludis/version_requirements.hh>
+
+#include <src/output/colour.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-decl.hh>
+
+#include <iostream>
+
+using namespace paludis;
+
+int
+do_fix_linkage(const tr1::shared_ptr<Environment> & env)
+{
+ Context ctx("When performing the Fix Linkage action:");
+
+ bool verbose(CommandLine::get_instance()->a_verbose.specified());
+ std::string library(CommandLine::get_instance()->a_library.argument());
+
+ if (library.empty())
+ std::cout << "Searching for broken packages... " << std::flush;
+ else
+ std::cout << "Searching for packages that depend on " << library << "... " << std::flush;
+ BrokenLinkageFinder finder(env.get(), library);
+ std::cout << std::endl;
+
+ if (finder.begin_broken_packages() == finder.end_broken_packages())
+ {
+ if (library.empty())
+ std::cout << "No broken packages found" << std::endl;
+ else
+ std::cout << "No packages that depend on " << library << " found" << std::endl;
+ }
+ else
+ {
+ if (library.empty())
+ std::cout << std::endl << colour(cl_heading, "Broken packages:") << std::endl;
+ else
+ std::cout << std::endl << colour(cl_heading, "Packages that depend on " + library + ":") << std::endl;
+ if (! verbose)
+ std::cout << std::endl;
+ }
+
+ tr1::shared_ptr<Sequence<std::string> > targets(new Sequence<std::string>);
+ for (BrokenLinkageFinder::BrokenPackageConstIterator pkg_it(finder.begin_broken_packages()),
+ pkg_it_end(finder.end_broken_packages()); pkg_it_end != pkg_it; ++pkg_it)
+ {
+ if (verbose)
+ std::cout << std::endl;
+
+ std::string pkgname(stringify((*pkg_it)->name()));
+ std::string fullname((*pkg_it)->canonical_form(idcf_full));
+ std::string::size_type pos(fullname.find(pkgname));
+ if (std::string::npos != pos)
+ fullname.replace(pos, pkgname.length(), colour(cl_package_name, pkgname));
+ std::cout << "* " << fullname << std::endl;
+
+ if (verbose)
+ for (BrokenLinkageFinder::BrokenFileConstIterator file_it(finder.begin_broken_files(*pkg_it)),
+ file_it_end(finder.end_broken_files(*pkg_it)); file_it_end != file_it; ++file_it)
+ {
+ std::cout << " " << *file_it;
+ if (library.empty())
+ std::cout << " (requires "
+ << join(finder.begin_missing_requirements(*pkg_it, *file_it),
+ finder.end_missing_requirements(*pkg_it, *file_it),
+ " ") << ")";
+ std::cout << std::endl;
+ }
+
+ targets->push_back(
+ stringify(
+ PackageDepSpec(
+ make_shared_ptr(new QualifiedPackageName((*pkg_it)->name())),
+ tr1::shared_ptr<CategoryNamePart>(),
+ tr1::shared_ptr<PackageNamePart>(),
+ CommandLine::get_instance()->a_exact.specified()
+ ? make_equal_to_version_requirements((*pkg_it)->version())
+ : tr1::shared_ptr<VersionRequirements>(),
+ vr_and,
+ make_shared_ptr(new SlotName((*pkg_it)->slot())))));
+ }
+
+ tr1::shared_ptr<const PackageID> orphans;
+ if (verbose && finder.begin_broken_files(orphans) != finder.end_broken_files(orphans))
+ {
+ if (library.empty())
+ std::cout << std::endl << "The following broken files are not owned by any installed package:" << std::endl;
+ else
+ std::cout << std::endl << "The following files that depend on " << library << " are not owned by any installed package:" << std::endl;
+
+ for (BrokenLinkageFinder::BrokenFileConstIterator file_it(finder.begin_broken_files(orphans)),
+ file_it_end(finder.end_broken_files(orphans)); file_it_end != file_it; ++file_it)
+ {
+ std::cout << " " << *file_it;
+ if (library.empty())
+ std::cout << " (requires "
+ << join(finder.begin_missing_requirements(orphans, *file_it),
+ finder.end_missing_requirements(orphans, *file_it),
+ " ") << ")";
+ std::cout << std::endl;
+ }
+ }
+
+ if (! targets->empty())
+ return do_install(env, targets);
+ return 0;
+}
+
diff --git a/src/clients/reconcilio/fix_linkage.hh b/src/clients/reconcilio/fix_linkage.hh
new file mode 100644
index 0000000..1f81c1d
--- /dev/null
+++ b/src/clients/reconcilio/fix_linkage.hh
@@ -0,0 +1,29 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_FIX_LINKAGE_HH
+#define PALUDIS_GUARD_RECONCILIO_FIX_LINKAGE_HH
+
+#include <paludis/util/tr1_memory.hh>
+
+#include <paludis/environment-fwd.hh>
+
+int do_fix_linkage(const paludis::tr1::shared_ptr<paludis::Environment> &);
+
+#endif
diff --git a/src/clients/reconcilio/install.cc b/src/clients/reconcilio/install.cc
new file mode 100644
index 0000000..6e380a4
--- /dev/null
+++ b/src/clients/reconcilio/install.cc
@@ -0,0 +1,131 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2005, 2006, 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "install.hh"
+#include "command_line.hh"
+
+#include <paludis/util/sequence-impl.hh>
+#include <paludis/util/tr1_functional.hh>
+
+#include <src/output/console_install_task.hh>
+
+#include <algorithm>
+#include <iostream>
+
+using namespace paludis;
+
+namespace
+{
+ class OurInstallTask :
+ public ConsoleInstallTask
+ {
+ private:
+ tr1::shared_ptr<Environment> _env;
+
+ public:
+ OurInstallTask(const tr1::shared_ptr<Environment> & env, const DepListOptions & options,
+ const tr1::shared_ptr<const DestinationsSet> & destinations) :
+ ConsoleInstallTask(env.get(), options, destinations),
+ _env(env)
+ {
+ }
+
+ virtual bool want_full_install_reasons() const
+ {
+ return CommandLine::get_instance()->install_args.want_full_install_reasons();
+ }
+
+ virtual bool want_tags_summary() const
+ {
+ return CommandLine::get_instance()->install_args.want_tags_summary();
+ }
+
+ virtual bool want_install_reasons() const
+ {
+ return CommandLine::get_instance()->install_args.want_install_reasons();
+ }
+
+ virtual bool want_unchanged_use_flags() const
+ {
+ return CommandLine::get_instance()->install_args.want_unchanged_use_flags();
+ }
+
+ virtual bool want_changed_use_flags() const
+ {
+ return CommandLine::get_instance()->install_args.want_changed_use_flags();
+ }
+
+ virtual bool want_new_use_flags() const
+ {
+ return CommandLine::get_instance()->install_args.want_new_use_flags();
+ }
+
+ virtual bool want_use_summary() const
+ {
+ return CommandLine::get_instance()->install_args.want_use_summary();
+ }
+
+ virtual std::string make_resume_command(const PackageIDSequence & s) const
+ {
+ std::string resume_command = environment()->paludis_command() + " --install";
+
+ resume_command = resume_command + CommandLine::get_instance()->install_args.resume_command_fragment(*this);
+ resume_command = resume_command + CommandLine::get_instance()->dl_args.resume_command_fragment(*this);
+
+ for (PackageIDSequence::ConstIterator i(s.begin()), i_end(s.end()) ;
+ i != i_end ; ++i)
+ resume_command = resume_command + " '=" + stringify(**i) + "'";
+
+ return resume_command;
+ }
+
+ void show_resume_command() const
+ {
+ if (CommandLine::get_instance()->install_args.a_fetch.specified() ||
+ CommandLine::get_instance()->install_args.a_pretend.specified())
+ return;
+
+ ConsoleInstallTask::show_resume_command(CommandLine::get_instance()->a_resume_command_template.argument());
+ }
+ };
+}
+
+int
+do_install(const tr1::shared_ptr<Environment> & env, const tr1::shared_ptr<const Sequence<std::string> > & targets)
+{
+ using namespace tr1::placeholders;
+
+ DepListOptions options;
+ CommandLine::get_instance()->dl_args.populate_dep_list_options(env.get(), options);
+ CommandLine::get_instance()->install_args.populate_dep_list_options(env.get(), options);
+
+ OurInstallTask task(env, options, CommandLine::get_instance()->install_args.destinations(env.get()));
+ CommandLine::get_instance()->install_args.populate_install_task(env.get(), task);
+ CommandLine::get_instance()->dl_args.populate_install_task(env.get(), task);
+
+ std::for_each(targets->begin(), targets->end(), tr1::bind(&InstallTask::add_target, tr1::ref(task), _1));
+
+ std::cout << std::endl;
+ task.execute();
+ std::cout << std::endl;
+
+ return task.exit_status();
+}
+
diff --git a/src/clients/reconcilio/install.hh b/src/clients/reconcilio/install.hh
new file mode 100644
index 0000000..de6038a
--- /dev/null
+++ b/src/clients/reconcilio/install.hh
@@ -0,0 +1,33 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_INSTALL_HH
+#define PALUDIS_GUARD_RECONCILIO_INSTALL_HH
+
+#include <paludis/util/sequence-fwd.hh>
+#include <paludis/util/tr1_memory.hh>
+
+#include <paludis/environment-fwd.hh>
+
+#include <string>
+
+int do_install(const paludis::tr1::shared_ptr<paludis::Environment> &,
+ const paludis::tr1::shared_ptr<const paludis::Sequence<std::string> > &);
+
+#endif
diff --git a/src/clients/reconcilio/littlelf/Elf.cc b/src/clients/reconcilio/littlelf/Elf.cc
new file mode 100644
index 0000000..9f05365
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/Elf.cc
@@ -0,0 +1,237 @@
+
+#include "Elf.hh"
+#include "ElfDynamicSection.hh"
+#include "ElfRelocationSection.hh"
+#include "ElfSymbolSection.hh"
+#include "ElfTypes.hh"
+
+#include <paludis/util/iterator.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/visitor-impl.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-impl.hh>
+
+#include <string>
+#include <exception>
+#include <istream>
+#include <vector>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <typename ElfType_>
+ struct Implementation<ElfObject<ElfType_> >
+ {
+ std::vector<paludis::tr1::shared_ptr<Section<ElfType_> > > sections;
+ };
+}
+
+namespace
+{
+ class StreamExceptions
+ {
+ private:
+ std::istream & _stream;
+ std::ios_base::iostate _old;
+
+ public:
+ StreamExceptions(std::istream & stream, std::ios_base::iostate flags) :
+ _stream(stream),
+ _old(stream.exceptions())
+ {
+ stream.exceptions(flags);
+ }
+
+ ~StreamExceptions()
+ {
+ _stream.exceptions(_old);
+ }
+ };
+
+ template <typename ElfType_>
+ class StringResolvingVisitor :
+ public SectionVisitor<ElfType_>
+ {
+ using SectionVisitor<ElfType_>::visit;
+
+ private:
+ ElfObject<ElfType_> *_elf_object;
+
+ public:
+ StringResolvingVisitor(ElfObject<ElfType_> * elf_object) :
+ _elf_object(elf_object)
+ {
+ }
+
+ virtual void visit(SymbolSection<ElfType_> & section)
+ {
+ section.resolve_symbols(*_elf_object->get_section_by_index(section.get_link_index()));
+ }
+
+ virtual void visit(DynamicSection<ElfType_> & section)
+ {
+ section.resolve_entry_names(*_elf_object->get_section_by_index(section.get_link_index()));
+ }
+ };
+}
+
+namespace littlelf_internals
+{
+ template <typename ElfType_>
+ class SectionNameResolvingVisitor :
+ public ConstSectionVisitor<ElfType_>
+ {
+ using ConstSectionVisitor<ElfType_>::visit;
+
+ private:
+ typename ElfObject<ElfType_>::SectionIterator _begin, _end;
+
+ public:
+ SectionNameResolvingVisitor(typename ElfObject<ElfType_>::SectionIterator begin, typename ElfObject<ElfType_>::SectionIterator end) :
+ _begin(begin),
+ _end(end)
+ {
+ }
+
+ virtual void visit(const StringSection<ElfType_> & section)
+ {
+ for (typename ElfObject<ElfType_>::SectionIterator i = _begin; i != _end; ++i)
+ i->resolve_section_name(section.get_string(i->get_name_index()));
+ }
+ };
+}
+
+InvalidElfFileError::InvalidElfFileError(const InvalidElfFileError & other) :
+ Exception(other)
+{
+}
+
+InvalidElfFileError::InvalidElfFileError() throw ():
+ Exception("Invalid ELF file")
+{
+}
+
+template <typename ElfType_>
+bool
+ElfObject<ElfType_>::is_valid_elf(std::istream & stream)
+{
+ StreamExceptions exns(stream, std::ios::eofbit | std::ios::failbit | std::ios::badbit);
+
+ try
+ {
+ stream.seekg(0, std::ios::beg);
+ if (stream.fail())
+ return false;
+
+ std::vector<char> ident(EI_NIDENT,0);
+ stream.read(&ident.front(), EI_NIDENT);
+
+ // Check the magic \177ELF bytes
+ if ( ! ( ( ident[EI_MAG0] == ELFMAG0)
+ && (ident[EI_MAG1] == ELFMAG1)
+ && (ident[EI_MAG2] == ELFMAG2)
+ && (ident[EI_MAG3] == ELFMAG3)
+ ) )
+ return false;
+
+ // Check the ELF file version
+ if (ident[EI_VERSION] != EV_CURRENT)
+ return false;
+
+ // Check whether the endianness is valid
+ if ((ident[EI_DATA] != ELFDATA2LSB) && (ident[EI_DATA] != ELFDATA2MSB))
+ return false;
+
+ return (ident[EI_CLASS] == ElfType_::elf_class);
+ }
+ catch (const std::ios_base::failure &)
+ {
+ return false;
+ }
+}
+
+template <typename ElfType_>
+ElfObject<ElfType_>::ElfObject(std::istream & stream) :
+ PrivateImplementationPattern<ElfObject>(new Implementation<ElfObject>)
+{
+ StreamExceptions exns(stream, std::ios::eofbit | std::ios::failbit | std::ios::badbit);
+
+ try
+ {
+ stream.seekg(0, std::ios::beg);
+ stream.read(reinterpret_cast<char *>(&_hdr), sizeof(typename ElfType_::Header));
+ stream.seekg(_hdr.e_shoff, std::ios::beg);
+ // The standard guarantees that there's at least one section
+ std::vector<typename ElfType_::SectionHeader> shdrs(_hdr.e_shnum);
+ stream.read(reinterpret_cast<char *>(&shdrs.front()), sizeof(typename ElfType_::SectionHeader) * _hdr.e_shnum);
+
+ for (typename std::vector<typename ElfType_::SectionHeader>::iterator i = shdrs.begin(); i != shdrs.end(); ++i)
+ {
+ if (i->sh_type == SHT_STRTAB)
+ _imp->sections.push_back(make_shared_ptr(new StringSection<ElfType_>(*i, stream)));
+ else if ( (i->sh_type == SHT_SYMTAB) || (i->sh_type == SHT_DYNSYM) )
+ _imp->sections.push_back(make_shared_ptr(new SymbolSection<ElfType_>(*i, stream)));
+ else if (i->sh_type == SHT_DYNAMIC)
+ _imp->sections.push_back(make_shared_ptr(new DynamicSection<ElfType_>(*i, stream)));
+ else if (i->sh_type == SHT_REL)
+ _imp->sections.push_back(make_shared_ptr(new RelocationSection<ElfType_, Relocation<ElfType_> >(*i, stream)));
+ else if (i->sh_type == SHT_RELA)
+ _imp->sections.push_back(make_shared_ptr(new RelocationSection<ElfType_, RelocationA<ElfType_> >(*i, stream)));
+ else
+ _imp->sections.push_back(make_shared_ptr(new GenericSection<ElfType_>(*i)));
+ }
+
+ if (! _hdr.e_shstrndx)
+ return;
+
+ littlelf_internals::SectionNameResolvingVisitor<ElfType_> res(section_begin(), section_end());
+ _imp->sections[_hdr.e_shstrndx]->accept(res);
+ }
+ catch (const std::ios_base::failure &)
+ {
+ throw InvalidElfFileError();
+ }
+}
+
+template <typename ElfType_>
+ElfObject<ElfType_>::~ElfObject()
+{
+}
+
+template <typename ElfType_>
+void
+ElfObject<ElfType_>::resolve_all_strings()
+{
+ StringResolvingVisitor<ElfType_> v(this);
+ for (SectionIterator i = section_begin(); i != section_end(); ++i)
+ i->accept(v);
+}
+
+template <typename ElfType_>
+typename ElfObject<ElfType_>::SectionIterator
+ElfObject<ElfType_>::section_begin() const
+{
+ return SectionIterator(indirect_iterator(_imp->sections.begin()));
+}
+
+template <typename ElfType_>
+typename ElfObject<ElfType_>::SectionIterator
+ElfObject<ElfType_>::section_end() const
+{
+ return SectionIterator(indirect_iterator(_imp->sections.end()));
+}
+
+template <typename ElfType_>
+typename ElfObject<ElfType_>::SectionIterator
+ElfObject<ElfType_>::get_section_by_index(unsigned int index) const
+{
+ if (index >= _imp->sections.size())
+ return SectionIterator(indirect_iterator(_imp->sections.end()));
+ return SectionIterator(indirect_iterator(_imp->sections.begin() + index));
+}
+
+template class ElfObject<Elf32Type>;
+template class ElfObject<Elf64Type>;
+
diff --git a/src/clients/reconcilio/littlelf/Elf.hh b/src/clients/reconcilio/littlelf/Elf.hh
new file mode 100644
index 0000000..d7e8192
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/Elf.hh
@@ -0,0 +1,103 @@
+#ifndef ELF_HH_
+#define ELF_HH_
+
+#include "ElfSections.hh"
+
+#include <paludis/util/exception.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-fwd.hh>
+
+#include <iosfwd>
+
+#include <elf.h>
+
+class InvalidElfFileError :
+ public paludis::Exception
+{
+ public:
+ InvalidElfFileError(const InvalidElfFileError &);
+ InvalidElfFileError() throw ();
+};
+
+template <typename ElfType_>
+class ElfObject :
+ private paludis::PrivateImplementationPattern<ElfObject<ElfType_> >
+{
+ using paludis::PrivateImplementationPattern<ElfObject>::_imp;
+
+ private:
+ typename ElfType_::Header _hdr;
+
+ public:
+ static bool is_valid_elf(std::istream & stream);
+ ElfObject(std::istream & stream);
+ ~ElfObject();
+
+ /**
+ * Returns e_type from the ELF header
+ */
+ unsigned int get_type() const
+ {
+ return _hdr.e_type;
+ }
+
+ /**
+ * Returns e_machine from the ELF header
+ */
+ unsigned int get_arch() const
+ {
+ return _hdr.e_machine;
+ }
+
+ /**
+ * Returns the OS ABI field from the ident field
+ */
+ unsigned char get_os_abi() const
+ {
+ return _hdr.e_ident[EI_OSABI];
+ }
+
+ /**
+ * Returns the OS ABI Version field from the ident field
+ */
+ unsigned char get_os_abi_version() const
+ {
+ return _hdr.e_ident[EI_ABIVERSION];
+ }
+
+ /**
+ * Returns the processor-specific flags
+ */
+ unsigned int get_flags() const
+ {
+ return _hdr.e_flags;
+ }
+
+ /**
+ * Returns whether this ELF file uses big-endian or little-endian
+ * Please note: If you didn't use is_valid_elf(...) to check whether
+ * this is an ELF object, this might be wrong
+ */
+ unsigned int is_big_endian() const
+ {
+ // We already checked in is_valid_elf_type whether it's valid or not
+ return (_hdr.e_ident[EI_DATA] == ELFDATA2MSB);
+ }
+
+ unsigned int get_number_of_sections() const
+ {
+ return _hdr.e_shnum;
+ }
+
+ typedef libwrapiter::ForwardIterator<ElfObject, Section<ElfType_> > SectionIterator;
+ SectionIterator section_begin() const;
+ SectionIterator section_end() const;
+
+ SectionIterator get_section_by_index(unsigned int index) const;
+
+ void resolve_all_strings();
+};
+
+
+#endif /*ELF_HH_*/
diff --git a/src/clients/reconcilio/littlelf/ElfDynamicSection.cc b/src/clients/reconcilio/littlelf/ElfDynamicSection.cc
new file mode 100644
index 0000000..870b3a3
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfDynamicSection.cc
@@ -0,0 +1,285 @@
+
+#include "ElfDynamicSection.hh"
+#include "ElfTypes.hh"
+
+#include <paludis/util/clone-impl.hh>
+#include <paludis/util/instantiation_policy-impl.hh>
+#include <paludis/util/iterator.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/visitor-impl.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-impl.hh>
+
+#include <istream>
+#include <map>
+#include <vector>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <typename ElfType_>
+ struct Implementation<DynamicEntries<ElfType_> >
+ {
+ std::map<typename ElfType_::DynamicTag, tr1::shared_ptr<DynamicEntry<ElfType_> > > available_types;
+ };
+
+ template <typename ElfType_>
+ struct Implementation<DynamicSection<ElfType_> >
+ {
+ std::vector<paludis::tr1::shared_ptr<DynamicEntry<ElfType_> > > dynamic_entries;
+ };
+}
+
+namespace littlelf_internals
+{
+ template <typename ElfType_>
+ class DynEntriesStringResolvingVisitor :
+ public DynamicEntriesVisitor<ElfType_>
+ {
+ using DynamicEntriesVisitor<ElfType_>::visit;
+
+ private:
+ const StringSection<ElfType_> & _string_section;
+
+ public:
+ DynEntriesStringResolvingVisitor(const StringSection<ElfType_> & string_section) :
+ _string_section(string_section)
+ {
+ }
+
+ virtual void visit(DynamicEntryString<ElfType_> & entry)
+ {
+ entry.resolve_string(_string_section.get_string(entry.get_string_index()));
+ }
+ };
+}
+
+namespace
+{
+ template <typename ElfType_>
+ class DynamicSectionStringResolvingVisitor :
+ public SectionVisitor<ElfType_>
+ {
+ using SectionVisitor<ElfType_>::visit;
+
+ private:
+ typename std::vector<tr1::shared_ptr<DynamicEntry<ElfType_> > >::iterator _begin, _end;
+
+ public:
+ DynamicSectionStringResolvingVisitor(
+ typename std::vector<tr1::shared_ptr<DynamicEntry<ElfType_> > >::iterator begin,
+ typename std::vector<tr1::shared_ptr<DynamicEntry<ElfType_> > >::iterator end) :
+ _begin(begin),
+ _end(end)
+ {
+ }
+
+ virtual void visit(StringSection<ElfType_> & section)
+ {
+ littlelf_internals::DynEntriesStringResolvingVisitor<ElfType_> v(section);
+ for(typename std::vector<tr1::shared_ptr<DynamicEntry<ElfType_> > >::iterator i = _begin; i != _end; ++i)
+ (*i)->accept(v);
+ }
+ };
+}
+
+template <typename ElfType_>
+DynamicEntry<ElfType_>::DynamicEntry(const std::string & my_tag_name) :
+ _tag_name(my_tag_name)
+{
+}
+
+template <typename ElfType_>
+DynamicEntry<ElfType_>::~DynamicEntry()
+{
+}
+
+template <typename ElfType_>
+DynamicEntryUnknown<ElfType_>::DynamicEntryUnknown() :
+ DynamicEntry<ElfType_>("unknown")
+{
+}
+
+template <typename ElfType_>
+DynamicEntryUnknown<ElfType_>::~DynamicEntryUnknown()
+{
+}
+
+template <typename ElfType_>
+void
+DynamicEntryUnknown<ElfType_>::initialize(const typename ElfType_::DynamicEntry &)
+{
+}
+
+template <typename ElfType_>
+DynamicEntryFlag<ElfType_>::DynamicEntryFlag(const std::string & name) :
+ DynamicEntry<ElfType_>(name)
+{
+}
+
+template <typename ElfType_>
+DynamicEntryFlag<ElfType_>::~DynamicEntryFlag()
+{
+}
+
+template <typename ElfType_>
+void
+DynamicEntryFlag<ElfType_>::initialize(const typename ElfType_::DynamicEntry &)
+{
+}
+
+template <typename ElfType_>
+DynamicEntryValue<ElfType_>::DynamicEntryValue(const std::string & name) :
+ DynamicEntry<ElfType_>(name)
+{
+}
+
+template <typename ElfType_>
+DynamicEntryValue<ElfType_>::~DynamicEntryValue()
+{
+}
+
+template <typename ElfType_>
+void
+DynamicEntryValue<ElfType_>::initialize(const typename ElfType_::DynamicEntry & entry)
+{
+ _value = entry.d_un.d_val;
+}
+
+template <typename ElfType_>
+DynamicEntryPointer<ElfType_>::DynamicEntryPointer(const std::string & name) :
+ DynamicEntry<ElfType_>(name)
+{
+}
+
+template <typename ElfType_>
+DynamicEntryPointer<ElfType_>::~DynamicEntryPointer()
+{
+}
+
+template <typename ElfType_>
+void
+DynamicEntryPointer<ElfType_>::initialize(const typename ElfType_::DynamicEntry & entry)
+{
+ _pointer = entry.d_un.d_ptr;
+}
+
+template <typename ElfType_>
+DynamicEntryString<ElfType_>::DynamicEntryString(const std::string & name) :
+ DynamicEntry<ElfType_>(name),
+ _str("")
+{
+}
+
+template <typename ElfType_>
+DynamicEntryString<ElfType_>::~DynamicEntryString()
+{
+}
+
+template <typename ElfType_>
+void
+DynamicEntryString<ElfType_>::initialize(const typename ElfType_::DynamicEntry & entry)
+{
+ _value = entry.d_un.d_val;
+}
+
+template <typename ElfType_>
+DynamicEntries<ElfType_>::DynamicEntries() :
+ PrivateImplementationPattern<DynamicEntries>(new Implementation<DynamicEntries>)
+{
+ register_type(DT_NEEDED, make_shared_ptr(new DynamicEntryString<ElfType_>("NEEDED")));
+ register_type(DT_RPATH, make_shared_ptr(new DynamicEntryString<ElfType_>("RPATH")));
+ register_type(DT_RUNPATH, make_shared_ptr(new DynamicEntryString<ElfType_>("RUNPATH")));
+ register_type(DT_SONAME, make_shared_ptr(new DynamicEntryString<ElfType_>("SONAME")));
+ register_type(DT_TEXTREL, make_shared_ptr(new DynamicEntryFlag<ElfType_>("TEXTREL")));
+ register_type(DT_NULL, make_shared_ptr(new DynamicEntryFlag<ElfType_>("NULL")));
+ register_type(DT_SYMTAB, make_shared_ptr(new DynamicEntryPointer<ElfType_>("SYMTAB")));
+ register_type(DT_STRTAB, make_shared_ptr(new DynamicEntryPointer<ElfType_>("STRTAB")));
+}
+
+template <typename ElfType_>
+DynamicEntries<ElfType_>::~DynamicEntries()
+{
+}
+
+template <typename ElfType_>
+void
+DynamicEntries<ElfType_>::register_type(typename ElfType_::DynamicTag identifier, tr1::shared_ptr<DynamicEntry<ElfType_> > entry)
+{
+ _imp->available_types[identifier] = entry;
+}
+
+template <typename ElfType_>
+tr1::shared_ptr<DynamicEntry<ElfType_> >
+DynamicEntries<ElfType_>::get_entry(typename ElfType_::DynamicTag tag) const
+{
+ typename std::map<typename ElfType_::DynamicTag, tr1::shared_ptr<DynamicEntry<ElfType_> > >::const_iterator i;
+ if (( i = _imp->available_types.find(tag)) != _imp->available_types.end())
+ return i->second->clone();
+ return make_shared_ptr(new DynamicEntryUnknown<ElfType_>());
+}
+
+template <typename ElfType_>
+bool
+DynamicEntries<ElfType_>::has_entry(typename ElfType_::DynamicTag identifier) const
+{
+ return ( _imp->available_types.find(identifier) != _imp->available_types.end() );
+}
+
+template <typename ElfType_>
+DynamicSection<ElfType_>::DynamicSection(const typename ElfType_::SectionHeader & shdr, std::istream & stream) :
+ Section<ElfType_>(shdr),
+ PrivateImplementationPattern<DynamicSection>(new Implementation<DynamicSection>)
+{
+ stream.seekg(shdr.sh_offset, std::ios::beg);
+ std::vector<typename ElfType_::DynamicEntry> tmp_entries(shdr.sh_size / shdr.sh_entsize);
+ stream.read( reinterpret_cast<char *>(&tmp_entries.front()), shdr.sh_size );
+
+ for (typename std::vector<typename ElfType_::DynamicEntry>::iterator i = tmp_entries.begin(); i != tmp_entries.end(); ++i)
+ {
+ paludis::tr1::shared_ptr<DynamicEntry<ElfType_> > instance(DynamicEntries<ElfType_>::get_instance()->get_entry(i->d_tag));
+ instance->initialize(*i);
+ _imp->dynamic_entries.push_back(instance);
+ }
+}
+
+template <typename ElfType_>
+DynamicSection<ElfType_>::~DynamicSection()
+{
+}
+
+template <typename ElfType_>
+std::string
+DynamicSection<ElfType_>::get_type() const
+{
+ static std::string type("DYNAMIC");
+ return type;
+}
+
+template <typename ElfType_>
+void
+DynamicSection<ElfType_>::resolve_entry_names(Section<ElfType_> & string_section)
+{
+ DynamicSectionStringResolvingVisitor<ElfType_> v(_imp->dynamic_entries.begin(), _imp->dynamic_entries.end());
+ string_section.accept(v);
+}
+
+template <typename ElfType_>
+typename DynamicSection<ElfType_>::EntryIterator
+DynamicSection<ElfType_>::entry_begin() const
+{
+ return EntryIterator(indirect_iterator(_imp->dynamic_entries.begin()));
+}
+
+template <typename ElfType_>
+typename DynamicSection<ElfType_>::EntryIterator
+DynamicSection<ElfType_>::entry_end() const
+{
+ return EntryIterator(indirect_iterator(_imp->dynamic_entries.end()));
+}
+
+template class DynamicSection<Elf32Type>;
+template class DynamicSection<Elf64Type>;
+
diff --git a/src/clients/reconcilio/littlelf/ElfDynamicSection.hh b/src/clients/reconcilio/littlelf/ElfDynamicSection.hh
new file mode 100644
index 0000000..0e5bf98
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfDynamicSection.hh
@@ -0,0 +1,210 @@
+#ifndef ELFDYNAMICSECTION_HH_
+#define ELFDYNAMICSECTION_HH_
+
+#include "ElfSections.hh"
+
+#include <paludis/util/clone.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/tr1_memory.hh>
+#include <paludis/util/visitor.hh>
+
+#include <string>
+#include <iosfwd>
+
+template <typename ElfType_> class DynamicEntry;
+template <typename ElfType_> class DynamicEntryUnknown;
+template <typename ElfType_> class DynamicEntryValue;
+template <typename ElfType_> class DynamicEntryPointer;
+template <typename ElfType_> class DynamicEntryString;
+template <typename ElfType_> class DynamicEntryFlag;
+
+template <typename ElfType_>
+struct DynamicEntryVisitorTypes :
+ paludis::VisitorTypes<
+ DynamicEntryVisitorTypes<ElfType_>,
+ DynamicEntry<ElfType_>,
+ DynamicEntryUnknown<ElfType_>,
+ DynamicEntryValue<ElfType_>,
+ DynamicEntryPointer<ElfType_>,
+ DynamicEntryString<ElfType_>,
+ DynamicEntryFlag<ElfType_>
+ >
+{
+};
+
+template <typename ElfType_>
+class DynamicEntriesVisitor :
+ public paludis::Visitor<DynamicEntryVisitorTypes<ElfType_> >
+{
+ public:
+ virtual void visit(DynamicEntryUnknown<ElfType_> &) {}
+ virtual void visit(DynamicEntryString<ElfType_> &) {}
+ virtual void visit(DynamicEntryPointer<ElfType_> &) {}
+ virtual void visit(DynamicEntryValue<ElfType_> &) {}
+ virtual void visit(DynamicEntryFlag<ElfType_> &) {}
+};
+
+template <typename ElfType_>
+class DynamicEntry :
+ public virtual paludis::AcceptInterface<DynamicEntryVisitorTypes<ElfType_> >,
+ public virtual paludis::Cloneable<DynamicEntry<ElfType_> >
+{
+ private:
+ std::string _tag_name;
+
+ public:
+ DynamicEntry(const std::string &);
+ ~DynamicEntry();
+ virtual void initialize(const typename ElfType_::DynamicEntry & entry) = 0;
+
+ std::string tag_name() const
+ {
+ return _tag_name;
+ }
+};
+
+template <typename ElfType_>
+class DynamicEntryUnknown :
+ public virtual DynamicEntry<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<DynamicEntryVisitorTypes<ElfType_> , DynamicEntryUnknown<ElfType_> >,
+ public paludis::CloneUsingThis<DynamicEntry<ElfType_>, DynamicEntryUnknown<ElfType_> >
+{
+ public:
+ DynamicEntryUnknown();
+ virtual ~DynamicEntryUnknown();
+ virtual void initialize(const typename ElfType_::DynamicEntry &);
+};
+
+template <typename ElfType_>
+class DynamicEntryFlag :
+ public virtual DynamicEntry<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<DynamicEntryVisitorTypes<ElfType_> , DynamicEntryFlag<ElfType_> >,
+ public paludis::CloneUsingThis<DynamicEntry<ElfType_>, DynamicEntryFlag<ElfType_> >
+{
+ public:
+ DynamicEntryFlag(const std::string &);
+ ~DynamicEntryFlag();
+ virtual void initialize(const typename ElfType_::DynamicEntry &);
+};
+
+template <typename ElfType_>
+class DynamicEntryValue :
+ public virtual DynamicEntry<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<DynamicEntryVisitorTypes<ElfType_> , DynamicEntryValue<ElfType_> >,
+ public paludis::CloneUsingThis<DynamicEntry<ElfType_>, DynamicEntryValue<ElfType_> >
+{
+ private:
+ typename ElfType_::DynamicValue _value;
+
+ public:
+ DynamicEntryValue(const std::string &);
+ virtual ~DynamicEntryValue();
+ virtual void initialize(const typename ElfType_::DynamicEntry & entry);
+
+ typename ElfType_::DynamicValue operator() () const
+ {
+ return _value;
+ }
+};
+
+template <typename ElfType_>
+class DynamicEntryPointer :
+ public virtual DynamicEntry<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<DynamicEntryVisitorTypes<ElfType_> , DynamicEntryPointer<ElfType_> >,
+ public paludis::CloneUsingThis<DynamicEntry<ElfType_>, DynamicEntryPointer<ElfType_> >
+{
+ private:
+ typename ElfType_::DynamicPointer _pointer;
+
+ public:
+ DynamicEntryPointer(const std::string &);
+ virtual ~DynamicEntryPointer();
+ virtual void initialize(const typename ElfType_::DynamicEntry &);
+
+ typename ElfType_::DynamicPointer operator() () const
+ {
+ return _pointer;
+ }
+};
+
+namespace littlelf_internals
+{
+ template <typename ElfType_> class DynEntriesStringResolvingVisitor;
+}
+
+template <typename ElfType_>
+class DynamicEntryString :
+ public virtual DynamicEntry<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<DynamicEntryVisitorTypes<ElfType_> , DynamicEntryString<ElfType_> >,
+ public paludis::CloneUsingThis<DynamicEntry<ElfType_>, DynamicEntryString<ElfType_> >
+{
+ friend class littlelf_internals::DynEntriesStringResolvingVisitor<ElfType_>;
+
+ private:
+ typename ElfType_::DynamicValue _value;
+ std::string _str;
+
+ public:
+ DynamicEntryString(const std::string &);
+ virtual ~DynamicEntryString();
+ virtual void initialize(const typename ElfType_::DynamicEntry &);
+
+ std::string operator() () const
+ {
+ return _str;
+ }
+
+ private:
+ void resolve_string(std::string str)
+ {
+ _str = str;
+ }
+
+ typename ElfType_::DynamicValue get_string_index() const
+ {
+ return _value;
+ }
+};
+
+template <typename ElfType_>
+class DynamicEntries :
+ public paludis::InstantiationPolicy<DynamicEntries<ElfType_>, paludis::instantiation_method::SingletonTag>,
+ private paludis::PrivateImplementationPattern<DynamicEntries<ElfType_> >
+{
+ using paludis::PrivateImplementationPattern<DynamicEntries>::_imp;
+ friend class paludis::InstantiationPolicy<DynamicEntries, paludis::instantiation_method::SingletonTag>;
+
+ public:
+ void register_type(typename ElfType_::DynamicTag, paludis::tr1::shared_ptr<DynamicEntry<ElfType_> >);
+
+ paludis::tr1::shared_ptr<DynamicEntry<ElfType_> > get_entry(typename ElfType_::DynamicTag) const;
+ bool has_entry(typename ElfType_::DynamicTag) const;
+
+ private:
+ DynamicEntries();
+ ~DynamicEntries();
+};
+
+template <typename ElfType_>
+class DynamicSection :
+ public Section<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<SectionVisitorTypes<ElfType_> , DynamicSection<ElfType_> >,
+ private paludis::PrivateImplementationPattern<DynamicSection<ElfType_> >
+{
+ using paludis::PrivateImplementationPattern<DynamicSection>::_imp;
+
+ public:
+ DynamicSection(const typename ElfType_::SectionHeader &, std::istream &);
+ virtual ~DynamicSection();
+
+ virtual std::string get_type() const;
+
+ void resolve_entry_names(Section<ElfType_> &);
+
+ typedef libwrapiter::ForwardIterator<DynamicSection, DynamicEntry<ElfType_> > EntryIterator;
+ EntryIterator entry_begin() const;
+ EntryIterator entry_end() const;
+};
+
+#endif /*ELFDYNAMICSECTION_HH_*/
diff --git a/src/clients/reconcilio/littlelf/ElfRelocationSection.cc b/src/clients/reconcilio/littlelf/ElfRelocationSection.cc
new file mode 100644
index 0000000..3771463
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfRelocationSection.cc
@@ -0,0 +1,84 @@
+
+#include "ElfRelocationSection.hh"
+#include "ElfTypes.hh"
+
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/visitor-impl.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-impl.hh>
+
+#include <istream>
+#include <vector>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <typename ElfType_, typename Relocation_>
+ struct Implementation<RelocationSection<ElfType_, Relocation_> >
+ {
+ std::vector<typename Relocation_::Entry> relocations;
+ };
+}
+
+template <typename ElfType_>
+RelocationEntry<ElfType_>::RelocationEntry(const typename ElfType_::Relocation & my_relocation) :
+ _my_relocation(my_relocation)
+{
+}
+
+template <typename ElfType_>
+RelocationEntry<ElfType_>::~RelocationEntry()
+{
+}
+
+template <typename ElfType_>
+RelocationAEntry<ElfType_>::RelocationAEntry(const typename ElfType_::RelocationA & my_relocation) :
+ _my_relocation(my_relocation)
+{
+}
+
+template <typename ElfType_>
+RelocationAEntry<ElfType_>::~RelocationAEntry()
+{
+}
+
+template <typename ElfType_> const std::string Relocation<ElfType_>::type_name = "REL";
+template <typename ElfType_> const std::string RelocationA<ElfType_>::type_name = "RELA";
+
+template <typename ElfType_, typename Relocation_>
+RelocationSection<ElfType_, Relocation_>::RelocationSection(const typename ElfType_::SectionHeader & shdr, std::istream & stream) :
+ Section<ElfType_>(shdr),
+ PrivateImplementationPattern<RelocationSection>(new Implementation<RelocationSection>)
+{
+ std::vector<typename Relocation_::Type> relocations(shdr.sh_size / shdr.sh_entsize);
+ stream.seekg(shdr.sh_offset, std::ios::beg);
+ stream.read(reinterpret_cast<char *>(&relocations.front()), shdr.sh_size);
+ for (typename std::vector<typename Relocation_::Type>::iterator i = relocations.begin(); i != relocations.end(); ++i)
+ _imp->relocations.push_back(typename Relocation_::Entry(*i));
+}
+
+template <typename ElfType_, typename Relocation_>
+RelocationSection<ElfType_, Relocation_>::~RelocationSection()
+{
+}
+
+template <typename ElfType_, typename Relocation_>
+typename RelocationSection<ElfType_, Relocation_>::RelocationIterator
+RelocationSection<ElfType_, Relocation_>::relocation_begin() const
+{
+ return RelocationIterator(_imp->relocations.begin());
+}
+
+template <typename ElfType_, typename Relocation_>
+typename RelocationSection<ElfType_, Relocation_>::RelocationIterator
+RelocationSection<ElfType_, Relocation_>::relocation_end() const
+{
+ return RelocationIterator(_imp->relocations.end());
+}
+
+template class RelocationSection<Elf32Type, Relocation<Elf32Type> >;
+template class RelocationSection<Elf32Type, RelocationA<Elf32Type> >;
+template class RelocationSection<Elf64Type, Relocation<Elf64Type> >;
+template class RelocationSection<Elf64Type, RelocationA<Elf64Type> >;
+
diff --git a/src/clients/reconcilio/littlelf/ElfRelocationSection.hh b/src/clients/reconcilio/littlelf/ElfRelocationSection.hh
new file mode 100644
index 0000000..e8009a2
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfRelocationSection.hh
@@ -0,0 +1,95 @@
+#ifndef ELFRELOCATIONSECTION_HH_
+#define ELFRELOCATIONSECTION_HH_
+
+#include "ElfSections.hh"
+
+#include <paludis/util/private_implementation_pattern.hh>
+
+#include <iosfwd>
+
+template <typename ElfType_>
+class RelocationEntry
+{
+ private:
+ typename ElfType_::Relocation _my_relocation;
+
+ public:
+ RelocationEntry(const typename ElfType_::Relocation &);
+ ~RelocationEntry();
+
+ typename ElfType_::RelocationOffset get_offset() const
+ {
+ return _my_relocation.r_offset;
+ }
+
+ typename ElfType_::RelocationInfo get_info() const
+ {
+ return _my_relocation.r_info;
+ }
+};
+
+template <typename ElfType_>
+class RelocationAEntry
+{
+ private:
+ typename ElfType_::RelocationA _my_relocation;
+
+ public:
+ RelocationAEntry(const typename ElfType_::RelocationA &);
+ ~RelocationAEntry();
+
+ typename ElfType_::RelocationOffset get_offset() const
+ {
+ return _my_relocation.r_offset;
+ }
+
+ typename ElfType_::RelocationInfo get_info() const
+ {
+ return _my_relocation.r_info;
+ }
+
+ typename ElfType_::RelocationAddend get_addend() const
+ {
+ return _my_relocation.r_addend;
+ }
+};
+
+template <typename ElfType_>
+struct Relocation
+{
+ typedef typename ElfType_::Relocation Type;
+ typedef RelocationEntry<ElfType_> Entry;
+ const static std::string type_name;
+};
+
+template <typename ElfType_>
+struct RelocationA
+{
+ typedef typename ElfType_::RelocationA Type;
+ typedef RelocationAEntry<ElfType_> Entry;
+ const static std::string type_name;
+};
+
+template <typename ElfType_, typename Relocation_>
+class RelocationSection :
+ public Section<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<SectionVisitorTypes<ElfType_> , RelocationSection<ElfType_, Relocation_> >,
+ private paludis::PrivateImplementationPattern<RelocationSection<ElfType_, Relocation_> >
+{
+ using paludis::PrivateImplementationPattern<RelocationSection>::_imp;
+
+ public:
+ RelocationSection(const typename ElfType_::SectionHeader &, std::istream &);
+ virtual ~RelocationSection();
+
+ virtual std::string get_type() const
+ {
+ return Relocation_::type_name;
+ }
+
+ typedef libwrapiter::ForwardIterator<RelocationSection, const typename Relocation_::Entry> RelocationIterator;
+ RelocationIterator relocation_begin() const;
+ RelocationIterator relocation_end() const;
+};
+
+#endif /*ELFRELOCATIONSECTION_HH_*/
diff --git a/src/clients/reconcilio/littlelf/ElfSections.cc b/src/clients/reconcilio/littlelf/ElfSections.cc
new file mode 100644
index 0000000..d744468
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfSections.cc
@@ -0,0 +1,81 @@
+
+#include "ElfSections.hh"
+#include "ElfTypes.hh"
+
+#include <paludis/util/visitor-impl.hh>
+
+#include <istream>
+#include <algorithm>
+
+using namespace paludis;
+
+template <typename ElfType_>
+Section<ElfType_>::Section(const typename ElfType_::SectionHeader & shdr) :
+ _shdr(shdr),
+ _name("")
+{
+}
+
+template <typename ElfType_>
+Section<ElfType_>::~Section()
+{
+}
+
+template <typename ElfType_>
+GenericSection<ElfType_>::GenericSection(const typename ElfType_::SectionHeader & shdr) :
+ Section<ElfType_>(shdr)
+{
+}
+
+template <typename ElfType_>
+GenericSection<ElfType_>::~GenericSection()
+{
+}
+
+template <typename ElfType_>
+std::string
+GenericSection<ElfType_>::get_type() const
+{
+ static std::string type("generic");
+ return type;
+}
+
+template <typename ElfType_>
+StringSection<ElfType_>::StringSection(const typename ElfType_::SectionHeader & shdr, std::istream & stream) :
+ Section<ElfType_>(shdr),
+ _stringTable(shdr.sh_size, ' ')
+{
+ std::string tmp_table(shdr.sh_size, '\0');
+ stream.seekg(shdr.sh_offset, std::ios::beg);
+ stream.read(&tmp_table[0], shdr.sh_size);
+ std::copy(tmp_table.begin(), tmp_table.end(), _stringTable.begin());
+}
+
+template <typename ElfType_>
+StringSection<ElfType_>::~StringSection()
+{
+}
+
+template <typename ElfType_>
+std::string
+StringSection<ElfType_>::get_string(typename ElfType_::Word index) const
+{
+ typename ElfType_::Word end(_stringTable.find_first_of('\0', index));
+ return _stringTable.substr(index, end-index);
+}
+
+template <typename ElfType_>
+std::string
+StringSection<ElfType_>::get_type() const
+{
+ static std::string type("STRTAB");
+ return type;
+}
+
+template class Section<Elf32Type>;
+template class Section<Elf64Type>;
+template class GenericSection<Elf32Type>;
+template class GenericSection<Elf64Type>;
+template class StringSection<Elf32Type>;
+template class StringSection<Elf64Type>;
+
diff --git a/src/clients/reconcilio/littlelf/ElfSections.hh b/src/clients/reconcilio/littlelf/ElfSections.hh
new file mode 100644
index 0000000..3d5f69b
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfSections.hh
@@ -0,0 +1,147 @@
+#ifndef ELFSECTIONS_HH_
+#define ELFSECTIONS_HH_
+
+#include <string>
+#include <iosfwd>
+
+#include <paludis/util/visitor.hh>
+
+template <typename ElfType_> class ElfObject;
+template <typename ElfType_> class Section;
+template <typename ElfType_> class StringSection;
+template <typename ElfType_> class DynamicSection;
+template <typename ElfType_> class SymbolSection;
+template <typename ElfType_> class GenericSection;
+template <typename ElfType_, class RelocationType_> class RelocationSection;
+
+template <typename ElfType_> class Relocation;
+template <typename ElfType_> class RelocationA;
+
+template <typename ElfType_>
+struct SectionVisitorTypes :
+ paludis::VisitorTypes<
+ SectionVisitorTypes<ElfType_>,
+ Section<ElfType_>,
+ StringSection<ElfType_>,
+ DynamicSection<ElfType_>,
+ SymbolSection<ElfType_>,
+ GenericSection<ElfType_>,
+ RelocationSection<ElfType_, Relocation<ElfType_> >,
+ RelocationSection<ElfType_, RelocationA<ElfType_> >
+ >
+{
+};
+
+template <typename ElfType_>
+class ConstSectionVisitor :
+ public paludis::ConstVisitor<SectionVisitorTypes<ElfType_> >
+{
+ public:
+ virtual void visit(const StringSection<ElfType_> &) {}
+ virtual void visit(const SymbolSection<ElfType_> &) {}
+ virtual void visit(const DynamicSection<ElfType_> &) {}
+ virtual void visit(const GenericSection<ElfType_> &) {}
+ virtual void visit(const RelocationSection<ElfType_, Relocation<ElfType_> > &) {}
+ virtual void visit(const RelocationSection<ElfType_, RelocationA<ElfType_> > &) {}
+};
+
+template <typename ElfType_>
+class SectionVisitor :
+ public paludis::Visitor<SectionVisitorTypes<ElfType_> >
+{
+ public:
+ virtual void visit(StringSection<ElfType_> &) {}
+ virtual void visit(SymbolSection<ElfType_> &) {}
+ virtual void visit(DynamicSection<ElfType_> &) {}
+ virtual void visit(GenericSection<ElfType_> &) {}
+ virtual void visit(RelocationSection<ElfType_, Relocation<ElfType_> > &) {}
+ virtual void visit(RelocationSection<ElfType_, RelocationA<ElfType_> > &) {}
+};
+
+namespace littlelf_internals
+{
+ template <typename ElfType_> class SectionNameResolvingVisitor;
+}
+
+template <typename ElfType_>
+class Section :
+ public virtual paludis::AcceptInterface<SectionVisitorTypes<ElfType_> >
+{
+ friend class littlelf_internals::SectionNameResolvingVisitor<ElfType_>;
+
+ private:
+ typename ElfType_::SectionHeader _shdr;
+ std::string _name;
+
+ protected:
+ void resolve_section_name(std::string name)
+ {
+ _name = name;
+ }
+
+ typename ElfType_::Word get_name_index() const
+ {
+ return _shdr.sh_name;
+ }
+
+ public:
+ Section(const typename ElfType_::SectionHeader &);
+ virtual ~Section();
+
+ virtual typename ElfType_::Offset get_data_offset() const
+ {
+ return _shdr.sh_offset;
+ }
+
+ virtual std::string get_name() const
+ {
+ return _name;
+ }
+ virtual std::string get_type() const = 0;
+
+ typename ElfType_::Word get_link_index() const
+ {
+ return _shdr.sh_link;
+ }
+
+ typename ElfType_::Address get_virtual_address() const
+ {
+ return _shdr.sh_addr;
+ }
+
+ typename ElfType_::SectionSize get_size() const
+ {
+ return _shdr.sh_size;
+ }
+};
+
+
+template <typename ElfType_>
+class GenericSection :
+ public Section<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<SectionVisitorTypes<ElfType_> , GenericSection<ElfType_> >
+{
+ public:
+ GenericSection(const typename ElfType_::SectionHeader &);
+ virtual ~GenericSection();
+ virtual std::string get_type() const;
+};
+
+template <typename ElfType_>
+class StringSection :
+ public Section<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<SectionVisitorTypes<ElfType_> , StringSection<ElfType_> >
+{
+ private:
+ std::string _stringTable;
+
+ public:
+ StringSection(const typename ElfType_::SectionHeader &, std::istream &);
+ virtual ~StringSection();
+
+ std::string get_string(typename ElfType_::Word) const;
+
+ virtual std::string get_type() const;
+};
+
+#endif /*ELFSECTIONS_HH_*/
diff --git a/src/clients/reconcilio/littlelf/ElfSymbolSection.cc b/src/clients/reconcilio/littlelf/ElfSymbolSection.cc
new file mode 100644
index 0000000..48e2d01
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfSymbolSection.cc
@@ -0,0 +1,155 @@
+
+#include "ElfSymbolSection.hh"
+#include "ElfTypes.hh"
+
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/visitor-impl.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-impl.hh>
+
+#include <istream>
+#include <vector>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <typename ElfType_>
+ struct Implementation<SymbolSection<ElfType_> >
+ {
+ std::vector<Symbol<ElfType_> > symbols;
+ };
+}
+
+namespace littlelf_internals
+{
+ template <typename ElfType_>
+ class SymbolStringResolvingVisitor :
+ public SectionVisitor<ElfType_>
+ {
+ using SectionVisitor<ElfType_>::visit;
+
+ private:
+ typename std::vector<Symbol<ElfType_> >::iterator _begin, _end;
+
+ public:
+ SymbolStringResolvingVisitor(typename std::vector<Symbol<ElfType_> >::iterator begin, typename std::vector<Symbol<ElfType_> >::iterator end) :
+ _begin(begin),
+ _end(end)
+ {
+ }
+
+ virtual void visit(StringSection<ElfType_> & section)
+ {
+ std::string str;
+ for (typename std::vector<Symbol<ElfType_> >::iterator i = _begin; i != _end; ++i)
+ i->resolve_symbol(section.get_string(i->get_symbol_index()));
+ }
+ };
+}
+
+template <typename ElfType_>
+Symbol<ElfType_>::Symbol(const typename ElfType_::Symbol & my_symbol) :
+ _my_symbol(my_symbol),
+ _symbol_name("(unresolved)"),
+ _binding("invalid"),
+ _visibility("invalid")
+{
+ switch (ELF64_ST_BIND(my_symbol.st_info))
+ {
+ case STB_LOCAL:
+ _binding = "local";
+ break;
+ case STB_GLOBAL:
+ _binding = "global";
+ break;
+ case STB_WEAK:
+ _binding = "weak";
+ break;
+ case STB_NUM:
+ _binding = "num";
+ break;
+ case STB_LOOS:
+ _binding = "loos";
+ break;
+ case STB_HIOS:
+ _binding = "hios";
+ break;
+ case STB_LOPROC:
+ _binding = "loproc";
+ break;
+ case STB_HIPROC:
+ _binding = "hiproc";
+ break;
+ }
+
+ switch (ELF64_ST_VISIBILITY(my_symbol.st_other))
+ {
+ case STV_DEFAULT:
+ _visibility = "default";
+ break;
+ case STV_INTERNAL:
+ _visibility = "internal";
+ break;
+ case STV_HIDDEN:
+ _visibility = "hidden";
+ break;
+ case STV_PROTECTED:
+ _visibility = "protected";
+ break;
+ }
+}
+
+template <typename ElfType_>
+Symbol<ElfType_>::~Symbol()
+{
+}
+
+template <typename ElfType_>
+SymbolSection<ElfType_>::SymbolSection(const typename ElfType_::SectionHeader & shdr, std::istream & stream) :
+ Section<ElfType_>(shdr),
+ PrivateImplementationPattern<SymbolSection>(new Implementation<SymbolSection>),
+ _type("invalid")
+{
+ if (shdr.sh_type == SHT_DYNSYM)
+ _type = "DYNSYM";
+ else if (shdr.sh_type == SHT_SYMTAB)
+ _type = "SYMTAB";
+
+ std::vector<typename ElfType_::Symbol> symbols(shdr.sh_size / shdr.sh_entsize);
+ stream.seekg(shdr.sh_offset, std::ios::beg);
+ stream.read( reinterpret_cast<char *>(&symbols.front()), shdr.sh_size );
+ for (typename std::vector<typename ElfType_::Symbol>::iterator i = symbols.begin(); i != symbols.end(); ++i)
+ _imp->symbols.push_back(Symbol<ElfType_>(*i));
+}
+
+template <typename ElfType_>
+SymbolSection<ElfType_>::~SymbolSection()
+{
+}
+
+template <typename ElfType_>
+void
+SymbolSection<ElfType_>::resolve_symbols(Section<ElfType_> & string_section)
+{
+ littlelf_internals::SymbolStringResolvingVisitor<ElfType_> v(_imp->symbols.begin(), _imp->symbols.end());
+ string_section.accept(v);
+}
+
+template <typename ElfType_>
+typename SymbolSection<ElfType_>::SymbolIterator
+SymbolSection<ElfType_>::symbol_begin() const
+{
+ return SymbolIterator(_imp->symbols.begin());
+}
+
+template <typename ElfType_>
+typename SymbolSection<ElfType_>::SymbolIterator
+SymbolSection<ElfType_>::symbol_end() const
+{
+ return SymbolIterator(_imp->symbols.end());
+}
+
+template class SymbolSection<Elf32Type>;
+template class SymbolSection<Elf64Type>;
+
diff --git a/src/clients/reconcilio/littlelf/ElfSymbolSection.hh b/src/clients/reconcilio/littlelf/ElfSymbolSection.hh
new file mode 100644
index 0000000..4588c71
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfSymbolSection.hh
@@ -0,0 +1,85 @@
+#ifndef ELFSYMBOLSECTION_HH_
+#define ELFSYMBOLSECTION_HH_
+
+#include "ElfSections.hh"
+
+#include <paludis/util/private_implementation_pattern.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator-fwd.hh>
+
+#include <iosfwd>
+
+namespace littlelf_internals
+{
+ template <typename ElfType_> class SymbolStringResolvingVisitor;
+}
+
+template <typename ElfType_>
+class Symbol
+{
+ friend class littlelf_internals::SymbolStringResolvingVisitor<ElfType_>;
+
+ private:
+ typename ElfType_::Symbol _my_symbol;
+ std::string _symbol_name;
+ std::string _binding, _visibility;
+
+ protected:
+ void resolve_symbol(std::string symbol_name)
+ {
+ _symbol_name = symbol_name;
+ }
+
+ typename ElfType_::Word get_symbol_index() const
+ {
+ return _my_symbol.st_name;
+ }
+
+ public:
+ Symbol(const typename ElfType_::Symbol &);
+ ~Symbol();
+
+ std::string name() const
+ {
+ return _symbol_name;
+ }
+
+ std::string binding() const
+ {
+ return _binding;
+ }
+
+ std::string visibility() const
+ {
+ return _visibility;
+ }
+};
+
+template <typename ElfType_>
+class SymbolSection :
+ public Section<ElfType_>,
+ public paludis::AcceptInterfaceVisitsThis<SectionVisitorTypes<ElfType_> , SymbolSection<ElfType_> >,
+ private paludis::PrivateImplementationPattern<SymbolSection<ElfType_> >
+{
+ using paludis::PrivateImplementationPattern<SymbolSection>::_imp;
+
+ private:
+ std::string _type;
+
+ public:
+ SymbolSection(const typename ElfType_::SectionHeader &, std::istream &);
+ virtual ~SymbolSection();
+
+ virtual std::string get_type() const
+ {
+ return _type;
+ }
+
+ void resolve_symbols(Section<ElfType_> &);
+
+ typedef libwrapiter::ForwardIterator<SymbolSection, const Symbol<ElfType_ > > SymbolIterator;
+ SymbolIterator symbol_begin() const;
+ SymbolIterator symbol_end() const;
+};
+
+#endif /*ELFSYMBOLSECTION_HH_*/
diff --git a/src/clients/reconcilio/littlelf/ElfTypes.hh b/src/clients/reconcilio/littlelf/ElfTypes.hh
new file mode 100644
index 0000000..3797197
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/ElfTypes.hh
@@ -0,0 +1,68 @@
+#ifndef ELFTYPES_HH_
+#define ELFTYPES_HH_
+
+#include <elf.h>
+
+struct Elf32Type
+{
+ enum {
+ elf_class = ELFCLASS32
+ };
+
+ typedef Elf32_Ehdr Header;
+ typedef Elf32_Shdr SectionHeader;
+ typedef Elf32_Sym Symbol;
+ typedef Elf32_Dyn DynamicEntry;
+ typedef Elf32_Rel Relocation;
+ typedef Elf32_Rela RelocationA;
+
+ typedef Elf32_Off Offset;
+ typedef Elf32_Half Half;
+ typedef Elf32_Word Word;
+ typedef Elf32_Xword Xword;
+ typedef Elf32_Sxword Sxword;
+ typedef Elf32_Addr Address;
+
+ typedef Elf32_Word DynamicValue;
+ typedef Elf32_Addr DynamicPointer;
+ typedef Elf32_Sword DynamicTag;
+
+ typedef Elf32_Addr RelocationOffset;
+ typedef Elf32_Word RelocationInfo;
+ typedef Elf32_Sword RelocationAddend;
+
+ typedef Elf32_Word SectionSize;
+};
+
+struct Elf64Type
+{
+ enum {
+ elf_class = ELFCLASS64
+ };
+
+ typedef Elf64_Ehdr Header;
+ typedef Elf64_Shdr SectionHeader;
+ typedef Elf64_Sym Symbol;
+ typedef Elf64_Dyn DynamicEntry;
+ typedef Elf64_Rel Relocation;
+ typedef Elf64_Rela RelocationA;
+
+ typedef Elf64_Off Offset;
+ typedef Elf64_Half Half;
+ typedef Elf64_Word Word;
+ typedef Elf64_Xword Xword;
+ typedef Elf64_Sxword Sxword;
+ typedef Elf64_Addr Address;
+
+ typedef Elf32_Xword DynamicValue;
+ typedef Elf64_Addr DynamicPointer;
+ typedef Elf64_Sxword DynamicTag;
+
+ typedef Elf64_Addr RelocationOffset;
+ typedef Elf64_Xword RelocationInfo;
+ typedef Elf64_Sxword RelocationAddend;
+
+ typedef Elf64_Xword SectionSize;
+};
+
+#endif /*ELFTYPES_HH_*/
diff --git a/src/clients/reconcilio/littlelf/Makefile.am b/src/clients/reconcilio/littlelf/Makefile.am
new file mode 100644
index 0000000..c7036fe
--- /dev/null
+++ b/src/clients/reconcilio/littlelf/Makefile.am
@@ -0,0 +1,32 @@
+AM_CXXFLAGS = -I$(top_srcdir) @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_NO_WOLD_STYLE_CAST@ @PALUDIS_CXXFLAGS_VISIBILITY@
+
+SUBDIRS = .
+
+noinst_LIBRARIES = liblittlelf.a
+
+liblittlelf_a_SOURCES = \
+ Elf.cc Elf.cc \
+ ElfSections.cc ElfSections.cc \
+ ElfDynamicSection.cc ElfDynamicSection.cc \
+ ElfRelocationSection.cc ElfRelocationSection.cc \
+ ElfSymbolSection.cc ElfSymbolSection.cc \
+ ElfTypes.hh
+
+TESTS_ENVIRONMENT = env \
+ TEST_SCRIPT_DIR="$(srcdir)/" \
+ TEST_OUTPUT_WRAPPER="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/outputwrapper`" \
+ bash $(top_srcdir)/test/run_test.sh bash
+
+TESTS =
+check_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = \
+ $(TESTS)
+
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda
+MAINTAINERCLEANFILES = Makefile.in
+
+built-sources : $(BUILT_SOURCES)
+ for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
+
+
diff --git a/src/clients/reconcilio/man_reconcilio.cc b/src/clients/reconcilio/man_reconcilio.cc
new file mode 100644
index 0000000..3501bb0
--- /dev/null
+++ b/src/clients/reconcilio/man_reconcilio.cc
@@ -0,0 +1,78 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006, 2007 Ciaran McCreesh <ciaranm@ciaranm.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 "command_line.hh"
+#include <paludis/args/man.hh>
+
+#include <iostream>
+#include <cstdlib>
+
+using std::cout;
+using std::endl;
+
+namespace
+{
+ struct ManCommandLine :
+ paludis::args::ArgsHandler
+ {
+ paludis::args::ArgsGroup group;
+ paludis::args::SwitchArg a_html;
+
+ ManCommandLine() :
+ group(this, "", ""),
+ a_html(&group, "html", '\0', "")
+ {
+ }
+
+ virtual std::string app_name() const
+ {
+ return "";
+ }
+
+ virtual std::string app_description() const
+ {
+ return "";
+ }
+
+ virtual std::string app_synopsis() const
+ {
+ return "";
+ }
+ };
+}
+
+int
+main(int argc, char * argv[])
+{
+ ManCommandLine cmdline;
+ cmdline.run(argc, argv, "", "", "");
+
+ if (cmdline.a_html.specified())
+ {
+ paludis::args::HtmlWriter hw(cout);
+ paludis::args::generate_doc(hw, CommandLine::get_instance());
+ }
+ else
+ {
+ paludis::args::ManWriter mw(cout);
+ paludis::args::generate_doc(mw, CommandLine::get_instance());
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/clients/reconcilio/reconcilio.cc b/src/clients/reconcilio/reconcilio.cc
new file mode 100644
index 0000000..4da7794c
--- /dev/null
+++ b/src/clients/reconcilio/reconcilio.cc
@@ -0,0 +1,160 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "command_line.hh"
+#include "fix_linkage.hh"
+
+#include <paludis/util/join.hh>
+#include <paludis/util/log.hh>
+
+#include <paludis/about.hh>
+#include <paludis/environment_maker.hh>
+#include <paludis/args/do_help.hh>
+
+#include <src/output/colour.hh>
+
+#include <iostream>
+
+using namespace paludis;
+
+namespace
+{
+ struct DoVersion
+ {
+ };
+
+ void display_version()
+ {
+ std::cout << "reconcilio" << " " << PALUDIS_VERSION_MAJOR << "."
+ << PALUDIS_VERSION_MINOR << "." << PALUDIS_VERSION_MICRO;
+ if (! std::string(PALUDIS_SUBVERSION_REVISION).empty())
+ std::cout << " svn " << PALUDIS_SUBVERSION_REVISION;
+ std::cout << std::endl << std::endl;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ Context ctx("In program " + join(argv, argv + argc, " ") + ":");
+
+ try
+ {
+ CommandLine::get_instance()->run(argc, argv, "reconcilio", "RECONCILIO_OPTIONS", "RECONCILIO_CMDLINE");
+
+ if (CommandLine::get_instance()->a_help.specified())
+ throw args::DoHelp();
+ if (CommandLine::get_instance()->a_version.specified())
+ throw DoVersion();
+
+ Log::get_instance()->set_program_name(argv[0]);
+ if (CommandLine::get_instance()->a_log_level.specified())
+ Log::get_instance()->set_log_level(CommandLine::get_instance()->a_log_level.option());
+
+ set_use_colour(! CommandLine::get_instance()->a_no_colour.specified());
+
+ std::string paludis_command("paludis");
+
+ if (CommandLine::get_instance()->a_environment.specified())
+ paludis_command.append(" --" + CommandLine::get_instance()->a_environment.long_name() + " " +
+ CommandLine::get_instance()->a_environment.argument());
+
+ paludis_command.append(" --" + CommandLine::get_instance()->a_log_level.long_name() + " " +
+ CommandLine::get_instance()->a_log_level.argument());
+
+ if (CommandLine::get_instance()->a_resume_command_template.specified())
+ paludis_command.append(" --" + CommandLine::get_instance()->a_resume_command_template.long_name() + " "
+ + CommandLine::get_instance()->a_resume_command_template.argument());
+
+ if (CommandLine::get_instance()->a_no_color.specified())
+ paludis_command.append(" --" + CommandLine::get_instance()->a_no_color.long_name());
+
+ paludis_command.append(CommandLine::get_instance()->install_args.paludis_command_fragment());
+ paludis_command.append(CommandLine::get_instance()->dl_args.paludis_command_fragment());
+
+ tr1::shared_ptr<Environment> env(
+ EnvironmentMaker::get_instance()->make_from_spec(
+ CommandLine::get_instance()->a_environment.argument()));
+ env->set_paludis_command(paludis_command);
+
+ return do_fix_linkage(env);
+ }
+
+ catch (const DoVersion &)
+ {
+ display_version();
+ std::cout << std::endl;
+ std::cout << "Paludis comes with ABSOLUTELY NO WARRANTY. Paludis is free software, and you" << std::endl;
+ std::cout << "are welcome to redistribute it under the terms of the GNU General Public" << std::endl;
+ std::cout << "License, version 2." << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+
+ catch (const args::ArgsError & e)
+ {
+ std::cerr << "Usage error: " << e.message() << std::endl;
+ std::cerr << "Try " << argv[0] << " --help" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ catch (const args::DoHelp & h)
+ {
+ if (h.message.empty())
+ {
+ std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
+ std::cout << std::endl;
+ std::cout << *CommandLine::get_instance();
+ return EXIT_SUCCESS;
+ }
+ else
+ {
+ std::cerr << "Usage error: " << h.message << std::endl;
+ std::cerr << "Try " << argv[0] << " --help" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ catch (const Exception & e)
+ {
+ std::cout << std::endl;
+ std::cerr << "Unhandled exception:" << std::endl
+ << " * " << e.backtrace("\n * ")
+ << e.message() << " (" << e.what() << ")" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ catch (const std::exception & e)
+ {
+ std::cout << std::endl;
+ std::cerr << "Unhandled exception:" << std::endl
+ << " * " << e.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ catch (...)
+ {
+ std::cout << std::endl;
+ std::cerr << "Unhandled exception:" << std::endl
+ << " * Unknown exception type. Ouch..." << std::endl;
+ return EXIT_FAILURE;
+ }
+}
+
diff --git a/src/clients/reconcilio/util/Makefile.am b/src/clients/reconcilio/util/Makefile.am
new file mode 100644
index 0000000..d9d8cd8
--- /dev/null
+++ b/src/clients/reconcilio/util/Makefile.am
@@ -0,0 +1,49 @@
+AM_CXXFLAGS = -I$(top_srcdir) @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_VISIBILITY@
+
+SUBDIRS = .
+
+noinst_LIBRARIES = libreconcilioutil.a
+
+libreconcilioutil_a_SOURCES = \
+ realpath.cc realpath.hh \
+ wildcard_expander.cc wildcard_expander.hh \
+ iterator.hh
+
+TESTS_ENVIRONMENT = env \
+ TEST_SCRIPT_DIR="$(srcdir)/" \
+ TEST_OUTPUT_WRAPPER="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/outputwrapper`" \
+ bash $(top_srcdir)/test/run_test.sh bash
+
+TESTS = iterator_TEST realpath_TEST wildcard_expander_TEST
+check_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = \
+ $(TESTS) \
+ realpath_TEST_setup.sh realpath_TEST_cleanup.sh \
+ wildcard_expander_TEST_setup.sh wildcard_expander_TEST_cleanup.sh
+
+iterator_TEST_LDADD = \
+ libreconcilioutil.a \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/util/test_extras.o \
+ $(top_builddir)/test/libtest.a
+
+realpath_TEST_LDADD = \
+ libreconcilioutil.a \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/util/test_extras.o \
+ $(top_builddir)/test/libtest.a
+
+wildcard_expander_TEST_LDADD = \
+ libreconcilioutil.a \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/util/test_extras.o \
+ $(top_builddir)/test/libtest.a
+
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda
+MAINTAINERCLEANFILES = Makefile.in
+
+built-sources : $(BUILT_SOURCES)
+ for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
+
+
diff --git a/src/clients/reconcilio/util/iterator.hh b/src/clients/reconcilio/util/iterator.hh
new file mode 100644
index 0000000..0baf47b
--- /dev/null
+++ b/src/clients/reconcilio/util/iterator.hh
@@ -0,0 +1,93 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_UTIL_ITERATOR_HH
+#define PALUDIS_GUARD_RECONCILIO_UTIL_ITERATOR_HH
+
+#include <paludis/util/operators.hh>
+
+#include <iterator>
+
+template <typename Value_, typename Iterator_>
+class MemberIterator :
+ public std::iterator<std::forward_iterator_tag, const Value_>,
+ public paludis::equality_operators::HasEqualityOperators
+{
+ private:
+ Iterator_ _it;
+ typedef Value_ std::iterator_traits<Iterator_>::value_type::* Member;
+ Member _member;
+
+ public:
+ MemberIterator(Iterator_ it, Member member) :
+ _it(it),
+ _member(member)
+ {
+ }
+
+ bool operator== (const MemberIterator & other) const
+ {
+ return _it == other._it;
+ }
+
+ MemberIterator & operator++ ()
+ {
+ ++_it;
+ return *this;
+ }
+
+ MemberIterator operator++ (int)
+ {
+ return MemberIterator(_it++, _member);
+ }
+
+ typename std::iterator_traits<MemberIterator>::reference operator* () const
+ {
+ return (*_it).*_member;
+ }
+
+ typename std::iterator_traits<MemberIterator>::pointer operator-> () const
+ {
+ return &((*_it).*_member);
+ }
+};
+
+template <typename Value_, typename Iterator_>
+inline MemberIterator<Value_, Iterator_>
+member_iterator(const Iterator_ & it, Value_ std::iterator_traits<Iterator_>::value_type::* member)
+{
+ return MemberIterator<Value_, Iterator_>(it, member);
+}
+
+template <typename Iterator_>
+inline MemberIterator<typename std::iterator_traits<Iterator_>::value_type::first_type, Iterator_>
+first_iterator(Iterator_ it)
+{
+ return member_iterator(it, &std::iterator_traits<Iterator_>::value_type::first);
+}
+
+template <typename Iterator_>
+inline MemberIterator<typename std::iterator_traits<Iterator_>::value_type::second_type, Iterator_>
+second_iterator(Iterator_ it)
+{
+ return member_iterator(it, &std::iterator_traits<Iterator_>::value_type::second);
+}
+
+#endif
+
diff --git a/src/clients/reconcilio/util/iterator_TEST.cc b/src/clients/reconcilio/util/iterator_TEST.cc
new file mode 100644
index 0000000..a589fb7
--- /dev/null
+++ b/src/clients/reconcilio/util/iterator_TEST.cc
@@ -0,0 +1,126 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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.hh"
+
+#include <paludis/util/join.hh>
+
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct FirstIteratorTest : TestCase
+ {
+ FirstIteratorTest() : TestCase("first_iterator") {}
+
+ void run()
+ {
+ typedef std::vector<std::pair<std::string, std::string> > V;
+
+ V v;
+ v.push_back(std::pair<std::string, std::string>("one", "I"));
+ v.push_back(std::pair<std::string, std::string>("two", "II"));
+ v.push_back(std::pair<std::string, std::string>("three", "III"));
+ v.push_back(std::pair<std::string, std::string>("four", "IV"));
+ v.push_back(std::pair<std::string, std::string>("five", "V"));
+
+ MemberIterator<std::string, V::iterator> it = first_iterator(v.begin());
+ TEST_CHECK(it == it);
+ TEST_CHECK(! (it != it));
+ TEST_CHECK_EQUAL(*it, "one");
+ TEST_CHECK_EQUAL(it->length(), 3U);
+
+ MemberIterator<std::string, V::iterator> it2(it);
+ TEST_CHECK(it == it2);
+ TEST_CHECK(! (it != it2));
+ TEST_CHECK_EQUAL(*++it2, "two");
+ TEST_CHECK_EQUAL(*it2, "two");
+ TEST_CHECK_EQUAL(it2->length(), 3U);
+ TEST_CHECK(it != it2);
+ TEST_CHECK(! (it == it2));
+
+ MemberIterator<std::string, V::iterator> it3(it2);
+ TEST_CHECK(it2 == it3++);
+ TEST_CHECK(it2 != it3);
+ TEST_CHECK_EQUAL(*it3, "three");
+ TEST_CHECK_EQUAL(it3->length(), 5U);
+
+ it3 = it2;
+ TEST_CHECK(it2 == it3);
+ TEST_CHECK_EQUAL(*it3, "two");
+ TEST_CHECK_EQUAL(*it3++, "two");
+
+ TEST_CHECK_EQUAL(join(first_iterator(v.begin()), first_iterator(v.end()), " "), "one two three four five");
+ }
+ } first_iterator_test;
+
+ struct SecondIteratorTest : TestCase
+ {
+ SecondIteratorTest() : TestCase("second_iterator") {}
+
+ void run()
+ {
+ typedef std::map<std::string, std::string> M;
+
+ M m;
+ m["I"] = "one";
+ m["II"] = "two";
+ m["III"] = "three";
+ m["IV"] = "four";
+ m["V"] = "five";
+
+ MemberIterator<std::string, M::iterator> it = second_iterator(m.begin());
+ TEST_CHECK(it == it);
+ TEST_CHECK(! (it != it));
+ TEST_CHECK_EQUAL(*it, "one");
+ TEST_CHECK_EQUAL(it->length(), 3U);
+
+ MemberIterator<std::string, M::iterator> it2(it);
+ TEST_CHECK(it == it2);
+ TEST_CHECK(! (it != it2));
+ TEST_CHECK_EQUAL(*++it2, "two");
+ TEST_CHECK_EQUAL(*it2, "two");
+ TEST_CHECK_EQUAL(it2->length(), 3U);
+ TEST_CHECK(it != it2);
+ TEST_CHECK(! (it == it2));
+
+ MemberIterator<std::string, M::iterator> it3(it2);
+ TEST_CHECK(it2 == it3++);
+ TEST_CHECK(it2 != it3);
+ TEST_CHECK_EQUAL(*it3, "three");
+ TEST_CHECK_EQUAL(it3->length(), 5U);
+
+ it3 = it2;
+ TEST_CHECK(it2 == it3);
+ TEST_CHECK_EQUAL(*it3, "two");
+ TEST_CHECK_EQUAL(*it3++, "two");
+
+ TEST_CHECK_EQUAL(join(second_iterator(m.begin()), second_iterator(m.end()), " "), "one two three four five");
+ }
+ } second_iterator_test;
+}
+
diff --git a/src/clients/reconcilio/util/realpath.cc b/src/clients/reconcilio/util/realpath.cc
new file mode 100644
index 0000000..b8342fd
--- /dev/null
+++ b/src/clients/reconcilio/util/realpath.cc
@@ -0,0 +1,96 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "realpath.hh"
+
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/stringify.hh>
+
+using namespace paludis;
+
+namespace
+{
+ FSEntry
+ do_realpath_with_current_and_root(
+ const FSEntry & file, const FSEntry & current, const FSEntry & root,
+ const FSEntry & orig_file, const FSEntry & orig_current, unsigned & symlinks)
+ {
+ if (symlinks >= 40)
+ throw FSError("Too many symlinks encountered while canonicalising '" + stringify(orig_file) +
+ "' relative to '" + stringify(orig_current) + "' with root '" + stringify(root) + "'");
+
+ FSEntry cur(current);
+
+ std::string file_str(stringify(file));
+ if ('/' == file_str[0])
+ {
+ file_str.erase(0, 1);
+ cur = FSEntry("/");
+ }
+
+ while (! file_str.empty())
+ {
+ std::string::size_type slash(file_str.find('/'));
+ if (0 == slash)
+ {
+ file_str.erase(0, 1);
+ continue;
+ }
+
+ std::string component(std::string::npos == slash
+ ? file_str : file_str.substr(0, slash));
+ if (std::string::npos == slash)
+ file_str.erase();
+ else
+ file_str.erase(0, slash + 1);
+
+ if ("." == component)
+ continue;
+ if (".." == component)
+ {
+ cur = cur.dirname();
+ continue;
+ }
+
+ FSEntry full(root / cur / component);
+ if (full.is_symbolic_link())
+ cur = do_realpath_with_current_and_root(FSEntry(full.readlink()), cur, root, orig_file, orig_current, ++symlinks);
+ else
+ cur = cur / component;
+ }
+
+ return cur;
+ }
+}
+
+FSEntry
+realpath_with_current_and_root(const FSEntry & file, const FSEntry & current, const FSEntry & root)
+{
+ unsigned symlinks(0);
+ return do_realpath_with_current_and_root(file, current, root, file, current, symlinks);
+}
+
+FSEntry
+dereference_with_root(const FSEntry & file, const FSEntry & root)
+{
+ if (file.is_symbolic_link())
+ return root / realpath_with_current_and_root(FSEntry(file.readlink()), file.dirname().strip_leading(root), root);
+ else
+ return file;
+}
diff --git a/src/clients/reconcilio/util/realpath.hh b/src/clients/reconcilio/util/realpath.hh
new file mode 100644
index 0000000..fb50010
--- /dev/null
+++ b/src/clients/reconcilio/util/realpath.hh
@@ -0,0 +1,32 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_UTIL_REALPATH_HH
+#define PALUDIS_GUARD_RECONCILIO_UTIL_REALPATH_HH
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/fs_entry-fwd.hh>
+
+paludis::FSEntry realpath_with_current_and_root(
+ const paludis::FSEntry &, const paludis::FSEntry &, const paludis::FSEntry &) PALUDIS_ATTRIBUTE((warn_unused_result));
+paludis::FSEntry dereference_with_root(const paludis::FSEntry &, const paludis::FSEntry &) PALUDIS_ATTRIBUTE((warn_unused_result));
+
+#endif
+
+
diff --git a/src/clients/reconcilio/util/realpath_TEST.cc b/src/clients/reconcilio/util/realpath_TEST.cc
new file mode 100644
index 0000000..522b6c4
--- /dev/null
+++ b/src/clients/reconcilio/util/realpath_TEST.cc
@@ -0,0 +1,68 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "realpath.hh"
+
+#include <paludis/util/fs_entry.hh>
+
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct RealpathWithCurrentAndRootTest : TestCase
+ {
+ RealpathWithCurrentAndRootTest() : TestCase("realpath_with_current_and_root") {}
+
+ FSEntry resolve(const FSEntry & symlink, const FSEntry & root)
+ {
+ return realpath_with_current_and_root(FSEntry((root / symlink).readlink()), symlink.dirname(), root);
+ }
+
+ void run()
+ {
+ FSEntry root("realpath_TEST_dir");
+
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libfoo.so"), root), "/usr/lib64/libfoo.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libbar.so"), root), "/usr/lib64/libbar.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libbaz.so"), root), "/usr/lib64/libbaz.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libxyzzy.so"), root), "/usr/lib64/libxyzzy.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libplugh.so"), root), "/usr/lib64/libplugh.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libplover.so"), root), "/usr/lib64/libplover.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libblast.so"), root), "/usr/lib64/libblast.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libquux.so"), root), "/usr/lib64/libquux.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libnarf.so"), root), "/usr/lib64/libnarf.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libblech.so"), root), "/usr/lib64/libblech.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libstab.so"), root), "/usr/lib64/libstab.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libsnark.so"), root), "/usr/lib64/libsnark.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libfool.so"), root), "/usr/lib32/libfool.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libbarf.so"), root), "/usr/lib64/barf/libbarf.so.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libblip.so"), root), "/usr/lib64/libblip.so.1.0.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/libpoing.so"), root), "/usr/lib64/libpoing.so.1.0.1");
+ TEST_CHECK_STRINGIFY_EQUAL(resolve(FSEntry("/usr/lib64/x/liby.so"), root), "/usr/lib64/x/liby.so.1");
+
+ TEST_CHECK_THROWS(resolve(FSEntry("/usr/lib64/libouch.so"), root),FSError);
+ TEST_CHECK_THROWS(resolve(FSEntry("/usr/lib64/libping.so"), root),FSError);
+ }
+ } realpath_with_current_and_root_test;
+}
+
diff --git a/src/clients/reconcilio/util/realpath_TEST_cleanup.sh b/src/clients/reconcilio/util/realpath_TEST_cleanup.sh
new file mode 100755
index 0000000..1a9922f
--- /dev/null
+++ b/src/clients/reconcilio/util/realpath_TEST_cleanup.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d realpath_TEST_dir ] ; then
+ rm -fr realpath_TEST_dir
+else
+ true
+fi
+
+
+
diff --git a/src/clients/reconcilio/util/realpath_TEST_setup.sh b/src/clients/reconcilio/util/realpath_TEST_setup.sh
new file mode 100755
index 0000000..1e22b10
--- /dev/null
+++ b/src/clients/reconcilio/util/realpath_TEST_setup.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir realpath_TEST_dir || exit 1
+cd realpath_TEST_dir || exit 1
+
+mkdir -p usr/lib64
+ln -s lib64 usr/lib
+
+touch usr/lib64/libfoo.so.1
+ln -s libfoo.so.1 usr/lib64/libfoo.so
+
+touch usr/lib64/libbar.so.1
+ln -s ../lib64/libbar.so.1 usr/lib64/libbar.so
+
+touch usr/lib64/libbaz.so.1
+ln -s /usr/lib64/libbaz.so.1 usr/lib64/libbaz.so
+
+touch usr/lib64/libxyzzy.so.1
+ln -s /usr/lib/libxyzzy.so.1 usr/lib64/libxyzzy.so
+
+touch usr/lib64/libplugh.so.1
+ln -s ./libplugh.so.1 usr/lib64/libplugh.so
+
+touch usr/lib64/libplover.so.1
+ln -s /usr/lib/../lib64/libplover.so.1 usr/lib64/libplover.so
+
+touch usr/lib64/libblast.so.1
+ln -s ../lib64/./libblast.so.1 usr/lib64/libblast.so
+
+touch usr/lib64/libquux.so.1
+ln -s //usr/lib64/libquux.so.1 usr/lib64/libquux.so
+
+touch usr/lib64/libnarf.so.1
+ln -s ../lib64//libnarf.so.1 usr/lib64/libnarf.so
+
+touch usr/lib64/libblech.so.1
+ln -s ../../../../../../../../../../usr/lib/libblech.so.1 usr/lib64/libblech.so
+
+ln -s libstab.so.1 usr/lib64/libstab.so
+
+ln -s /usr/lib/libsnark.so.1 usr/lib64/libsnark.so
+
+ln -s /usr/lib32/libfool.so.1 usr/lib64/libfool.so
+
+ln -s ../lib/barf/libbarf.so.1 usr/lib64/libbarf.so
+
+touch usr/lib64/libblip.so.1.0.1
+ln -s libblip.so.1.0.1 usr/lib64/libblip.so.1
+ln -s libblip.so.1 usr/lib64/libblip.so
+
+touch usr/lib64/libpoing.so.1.0.1
+ln -s /usr/lib64/libpoing.so.1.0.1 usr/lib64/libpoing.so.1
+ln -s /usr/lib/libpoing.so.1 usr/lib64/libpoing.so
+
+mkdir usr/lib64/x
+touch usr/lib64/x/liby.so.1
+ln -s /usr/lib64/x/liby.so.1 usr/lib64/x/liby.so
+
+ln -s libouch.so usr/lib64/libouch.so
+
+ln -s libping.so usr/lib64/libpong.so
+ln -s libpong.so usr/lib64/libping.so
+
diff --git a/src/clients/reconcilio/util/wildcard_expander.cc b/src/clients/reconcilio/util/wildcard_expander.cc
new file mode 100644
index 0000000..7dac617
--- /dev/null
+++ b/src/clients/reconcilio/util/wildcard_expander.cc
@@ -0,0 +1,185 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "wildcard_expander.hh"
+
+#include <paludis/util/exception.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/tr1_memory.hh>
+
+#include <vector>
+
+#include <cerrno>
+#include <glob.h>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<WildcardExpander>
+ {
+ tr1::shared_ptr<std::vector<FSEntry> > files;
+ std::vector<FSEntry>::const_iterator it;
+
+ Implementation() :
+ files(new std::vector<FSEntry>)
+ {
+ }
+
+ Implementation(const tr1::shared_ptr<std::vector<FSEntry> > & the_files,
+ std::vector<FSEntry>::const_iterator the_it) :
+ files(the_files),
+ it(the_it)
+ {
+ }
+ };
+}
+
+WildcardExpansionError::WildcardExpansionError(const std::string & the_message) throw () :
+ FSError(the_message)
+{
+}
+
+namespace
+{
+ struct Glob
+ {
+ glob_t g;
+
+ Glob(const std::string &, int);
+
+ const glob_t & operator* ()
+ {
+ return g;
+ }
+
+ const glob_t * operator-> ()
+ {
+ return &g;
+ }
+
+ ~Glob()
+ {
+ globfree(&g);
+ }
+ };
+
+ Glob::Glob(const std::string & pattern, int flags)
+ {
+ int ret(glob(pattern.c_str(), flags, 0, &g));
+ switch (ret)
+ {
+ case 0:
+ case GLOB_NOMATCH:
+ break;
+
+ case GLOB_NOSPACE:
+ globfree(&g);
+ throw std::bad_alloc();
+
+ case GLOB_ABORTED:
+ globfree(&g);
+ throw WildcardExpansionError("Error running glob(): " + stringify(strerror(errno)));
+
+ default:
+ globfree(&g);
+ throw InternalError(PALUDIS_HERE, "glob() returned unrecognised value " + stringify(ret));
+ }
+ }
+}
+
+WildcardExpander::WildcardExpander(const std::string & pattern, const paludis::FSEntry & root) :
+ PrivateImplementationPattern<WildcardExpander>(new Implementation<WildcardExpander>)
+{
+ // GLOB_NOCHECK seems to be buggy in glibc 2.5: fails
+ // TEST_CHECK_EQUAL(expand("/foo\\*"), "/foo*");
+ // with
+ // Expected 'expand("/foo\\*")' to equal '/foo*' but got '/foo\*'
+ Glob g(stringify(root / pattern), 0);
+ if (0 < g->gl_pathc)
+ for (unsigned i = 0; i < g->gl_pathc; ++i)
+ _imp->files->push_back(FSEntry(g->gl_pathv[i]).strip_leading(root));
+ else
+ _imp->files->push_back(FSEntry("/") / pattern);
+
+ _imp->it = _imp->files->begin();
+}
+
+WildcardExpander::WildcardExpander() :
+ PrivateImplementationPattern<WildcardExpander>(new Implementation<WildcardExpander>)
+{
+ _imp->it = _imp->files->end();
+}
+
+WildcardExpander::WildcardExpander(const WildcardExpander & other) :
+ PrivateImplementationPattern<WildcardExpander>(
+ new Implementation<WildcardExpander>(other._imp->files, other._imp->it))
+{
+}
+
+WildcardExpander::~WildcardExpander()
+{
+}
+
+const WildcardExpander &
+WildcardExpander::operator= (const WildcardExpander & other)
+{
+ _imp->files = other._imp->files;
+ _imp->it = other._imp->it;
+ return *this;
+}
+
+const FSEntry &
+WildcardExpander::operator* () const
+{
+ return *_imp->it;
+}
+
+const FSEntry *
+WildcardExpander::operator-> () const
+{
+ return &*_imp->it;
+}
+
+WildcardExpander &
+WildcardExpander::operator++ ()
+{
+ ++_imp->it;
+ return *this;
+}
+
+WildcardExpander
+WildcardExpander::operator++ (int)
+{
+ WildcardExpander copy(*this);
+ ++_imp->it;
+ return copy;
+}
+
+bool
+WildcardExpander::operator== (const WildcardExpander & other) const
+{
+ if (_imp->it == _imp->files->end() && other._imp->it == other._imp->files->end())
+ return true;
+ return _imp->files == other._imp->files && _imp->it == other._imp->it;
+}
+
diff --git a/src/clients/reconcilio/util/wildcard_expander.hh b/src/clients/reconcilio/util/wildcard_expander.hh
new file mode 100644
index 0000000..5fbc7bd
--- /dev/null
+++ b/src/clients/reconcilio/util/wildcard_expander.hh
@@ -0,0 +1,60 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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_RECONCILIO_UTIL_WILDCARD_EXPANDER_HH
+#define PALUDIS_GUARD_RECONCILIO_UTIL_WILDCARD_EXPANDER_HH
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/operators.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+#include <iterator>
+
+class PALUDIS_VISIBLE WildcardExpansionError : public paludis::FSError
+{
+ public:
+ WildcardExpansionError(const std::string & message) throw ();
+};
+
+class PALUDIS_VISIBLE WildcardExpander :
+ public std::iterator<std::forward_iterator_tag, const paludis::FSEntry>,
+ public paludis::equality_operators::HasEqualityOperators,
+ private paludis::PrivateImplementationPattern<WildcardExpander>
+{
+ public:
+ WildcardExpander(const std::string &, const paludis::FSEntry & = paludis::FSEntry("/"));
+ WildcardExpander();
+ WildcardExpander(const WildcardExpander &);
+
+ ~WildcardExpander();
+
+ const WildcardExpander & operator= (const WildcardExpander &);
+
+ const paludis::FSEntry & operator* () const PALUDIS_ATTRIBUTE((warn_unused_result));
+ const paludis::FSEntry * operator-> () const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ WildcardExpander & operator++ ();
+ WildcardExpander operator++ (int);
+
+ bool operator== (const WildcardExpander &) const PALUDIS_ATTRIBUTE((warn_unused_result));
+};
+
+#endif
+
diff --git a/src/clients/reconcilio/util/wildcard_expander_TEST.cc b/src/clients/reconcilio/util/wildcard_expander_TEST.cc
new file mode 100644
index 0000000..0378745
--- /dev/null
+++ b/src/clients/reconcilio/util/wildcard_expander_TEST.cc
@@ -0,0 +1,104 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 David Leverton <levertond@googlemail.com>
+ *
+ * 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 "wildcard_expander.hh"
+
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/join.hh>
+
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct WildcardExpanderGeneralTest : TestCase
+ {
+ WildcardExpanderGeneralTest() : TestCase("WildcardExpander general") {}
+
+ std::string expand(const std::string & pattern)
+ {
+ return join(WildcardExpander(pattern, FSEntry::cwd() / "wildcard_expander_TEST_dir"),
+ WildcardExpander(), " ");
+ }
+
+ void run()
+ {
+ FSEntry myroot(FSEntry::cwd() / "wildcard_expander_TEST_dir");
+
+ TEST_CHECK_EQUAL(expand("/xyz*zy"), "/xyz1zy /xyz22zy /xyzzy");
+ TEST_CHECK_EQUAL(expand("/plugh"), "/plugh");
+ TEST_CHECK_EQUAL(expand("/quux"), "/quux");
+ TEST_CHECK_EQUAL(expand("/quux*quux"), "/quux*quux");
+ TEST_CHECK_EQUAL(expand("/meh/?"), "/meh/1 /meh/2 /meh/3");
+ TEST_CHECK_EQUAL(expand("/foo*\\*"), "/foo* /foo123*");
+ TEST_CHECK_EQUAL(expand("/foo\\*"), "/foo*");
+ }
+ } wildcard_expander_general_test;
+
+ struct WildcardExpanderIteratorSanityTest : TestCase
+ {
+ WildcardExpanderIteratorSanityTest() : TestCase("WildcardExpander iterator sanity") {}
+
+ void run()
+ {
+ WildcardExpander it("/foo*bar", FSEntry::cwd() / "wildcard_expander_TEST_dir");
+ TEST_CHECK(it == it);
+ TEST_CHECK(! (it != it));
+ TEST_CHECK(it != WildcardExpander());
+ TEST_CHECK_STRINGIFY_EQUAL(*it, "/fooAbar");
+ TEST_CHECK_EQUAL(it->basename(), "fooAbar");
+
+ WildcardExpander it2(it);
+ TEST_CHECK(it == it2);
+ TEST_CHECK(! (it != it2));
+ TEST_CHECK(it2 != WildcardExpander());
+ TEST_CHECK_STRINGIFY_EQUAL(*++it2, "/fooBbar");
+ TEST_CHECK_STRINGIFY_EQUAL(*it2, "/fooBbar");
+ TEST_CHECK_EQUAL(it2->basename(), "fooBbar");
+ TEST_CHECK(it != it2);
+ TEST_CHECK(! (it == it2));
+ TEST_CHECK(it2 != WildcardExpander());
+
+ WildcardExpander it3(it2);
+ TEST_CHECK(it2 == it3++);
+ TEST_CHECK(it2 != it3);
+ TEST_CHECK(it3 != WildcardExpander());
+ TEST_CHECK_STRINGIFY_EQUAL(*it3, "/fooCbar");
+ TEST_CHECK_EQUAL(it3->basename(), "fooCbar");
+
+ it3 = it2;
+ TEST_CHECK(it2 == it3);
+ TEST_CHECK_STRINGIFY_EQUAL(*it3, "/fooBbar");
+ TEST_CHECK_STRINGIFY_EQUAL(*it3++, "/fooBbar");
+ TEST_CHECK(it3 != WildcardExpander());
+
+ TEST_CHECK(++it3 != WildcardExpander());
+ TEST_CHECK(++it3 != WildcardExpander());
+ TEST_CHECK(++it3 == WildcardExpander());
+
+ TEST_CHECK_EQUAL(join(WildcardExpander("/foo*bar", FSEntry::cwd() / "wildcard_expander_TEST_dir"),
+ WildcardExpander(), " "),
+ "/fooAbar /fooBbar /fooCbar /fooDbar /fooEbar");
+ }
+ } wildcard_expander_iterator_sanity_test;
+}
+
diff --git a/src/clients/reconcilio/util/wildcard_expander_TEST_cleanup.sh b/src/clients/reconcilio/util/wildcard_expander_TEST_cleanup.sh
new file mode 100755
index 0000000..39d9364
--- /dev/null
+++ b/src/clients/reconcilio/util/wildcard_expander_TEST_cleanup.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d wildcard_expander_TEST_dir ] ; then
+ rm -fr wildcard_expander_TEST_dir
+else
+ true
+fi
+
+
+
diff --git a/src/clients/reconcilio/util/wildcard_expander_TEST_setup.sh b/src/clients/reconcilio/util/wildcard_expander_TEST_setup.sh
new file mode 100755
index 0000000..a62b04d
--- /dev/null
+++ b/src/clients/reconcilio/util/wildcard_expander_TEST_setup.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir wildcard_expander_TEST_dir || exit 1
+cd wildcard_expander_TEST_dir || exit 1
+
+touch xyzzy xyz1zy xyz22zy
+mkdir xyz123
+touch xyz123/456zy
+
+touch plugh
+
+mkdir meh
+touch meh/1 meh/2 meh/3 meh/44
+
+touch foo123\* foo\*
+
+touch fooAbar fooBbar fooCbar fooDbar fooEbar
+