aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-10-13 05:56:39 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-10-13 05:56:39 +0000
commite6bd29a36ef60e70ee89eea15a4d09e3f731bbca (patch)
tree6dbc82f7b97bea8316919896e947906c65cb0bdf
parent01dddc88626816498fad0c78103530d892fd0051 (diff)
downloadpaludis-e6bd29a36ef60e70ee89eea15a4d09e3f731bbca.tar.gz
paludis-e6bd29a36ef60e70ee89eea15a4d09e3f731bbca.tar.xz
Add UnpackagedRepository, for managing unpackaged images, and importare, a client for installing unmanaged images. Fixes: ticket:399
-rw-r--r--configure.ac8
-rw-r--r--paludis/repositories/unpackaged/Makefile.am169
-rw-r--r--paludis/repositories/unpackaged/exceptions.cc29
-rw-r--r--paludis/repositories/unpackaged/exceptions.hh38
-rw-r--r--paludis/repositories/unpackaged/installed_unpackaged_id.cc589
-rw-r--r--paludis/repositories/unpackaged/installed_unpackaged_id.hh95
-rw-r--r--paludis/repositories/unpackaged/installed_unpackaged_repository.cc318
-rw-r--r--paludis/repositories/unpackaged/installed_unpackaged_repository.hh92
-rw-r--r--paludis/repositories/unpackaged/installed_unpackaged_repository.sr14
-rw-r--r--paludis/repositories/unpackaged/installed_unpackaged_repository_TEST.cc596
-rwxr-xr-xpaludis/repositories/unpackaged/installed_unpackaged_repository_TEST_cleanup.sh9
-rwxr-xr-xpaludis/repositories/unpackaged/installed_unpackaged_repository_TEST_setup.sh60
-rw-r--r--paludis/repositories/unpackaged/ndbam-fwd.hh31
-rw-r--r--paludis/repositories/unpackaged/ndbam.cc539
-rw-r--r--paludis/repositories/unpackaged/ndbam.hh82
-rwxr-xr-xpaludis/repositories/unpackaged/ndbam.sr18
-rw-r--r--paludis/repositories/unpackaged/ndbam_merger.cc298
-rw-r--r--paludis/repositories/unpackaged/ndbam_merger.hh62
-rw-r--r--paludis/repositories/unpackaged/ndbam_merger.sr18
-rw-r--r--paludis/repositories/unpackaged/ndbam_unmerger.cc295
-rw-r--r--paludis/repositories/unpackaged/ndbam_unmerger.hh84
-rw-r--r--paludis/repositories/unpackaged/ndbam_unmerger.sr19
-rw-r--r--paludis/repositories/unpackaged/registration.cc97
-rw-r--r--paludis/repositories/unpackaged/unpackaged_id.cc320
-rw-r--r--paludis/repositories/unpackaged/unpackaged_id.hh90
-rw-r--r--paludis/repositories/unpackaged/unpackaged_repository.cc140
-rw-r--r--paludis/repositories/unpackaged/unpackaged_repository.hh74
-rw-r--r--paludis/repositories/unpackaged/unpackaged_repository.sr16
-rw-r--r--paludis/repositories/unpackaged/unpackaged_repository_TEST.cc207
-rwxr-xr-xpaludis/repositories/unpackaged/unpackaged_repository_TEST_cleanup.sh9
-rwxr-xr-xpaludis/repositories/unpackaged/unpackaged_repository_TEST_setup.sh15
-rw-r--r--src/clients/importare/Makefile.am73
-rw-r--r--src/clients/importare/command_line.cc91
-rw-r--r--src/clients/importare/command_line.hh62
-rw-r--r--src/clients/importare/importare.cc209
-rw-r--r--src/clients/importare/install.cc136
-rw-r--r--src/clients/importare/install.hh31
-rw-r--r--src/clients/importare/man_importare.cc79
38 files changed, 5110 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 6945848..ebd0eb5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -952,7 +952,7 @@ AC_SUBST([VIM_INSTALL_DIR])
dnl }}}
dnl {{{ repositories
-ALL_REPOSITORIES="cran e fake gems virtuals"
+ALL_REPOSITORIES="cran e fake gems unpackaged virtuals"
DEFAULT_REPOSITORIES="e fake virtuals"
AC_MSG_CHECKING([which repositories to build...])
AC_ARG_WITH([repositories],
@@ -965,6 +965,7 @@ AC_ARG_WITH([repositories],
e Ebuild, Ebin, Exheres, VDB etc
fake A fake repository for test cases and adjutrix
gems Ruby gems (experimental)
+ unpackaged Unpackaged (experimental, for importare)
virtuals Old style Gentoo virtuals],
[repositories="`echo $with_repositories | tr ',' ' '`"],
[repositories="$DEFAULT_REPOSITORIES"])
@@ -1022,7 +1023,7 @@ AC_DEFINE_UNQUOTED([DEFAULT_DISTRIBUTION], "$DEFAULT_DISTRIBUTION", [Default dis
dnl }}}
dnl {{{ clients
-ALL_CLIENTS="accerso adjutrix contrarius gtkpaludis inquisitio instruo paludis qualudis reconcilio"
+ALL_CLIENTS="accerso adjutrix contrarius gtkpaludis importare inquisitio instruo paludis qualudis reconcilio"
DEFAULT_CLIENTS="adjutrix paludis reconcilio"
if test x"$ENABLE_QA" = "xyes" ; then
DEFAULT_CLIENTS="$DEFAULT_CLIENTS qualudis"
@@ -1039,6 +1040,7 @@ AC_ARG_WITH([clients],
adjutrix A tool for tree querying
contrarius The cross toolchain creator
gtkpaludis GUI client (experimental)
+ importare On-the-fly unpackaged installer
inquisitio A search client
instruo A metadata generation client
paludis The Paludis console client
@@ -1305,6 +1307,7 @@ AC_OUTPUT(
paludis/repositories/e/qa/Makefile
paludis/repositories/fake/Makefile
paludis/repositories/gems/Makefile
+ paludis/repositories/unpackaged/Makefile
paludis/repositories/virtuals/Makefile
paludis/selinux/Makefile
paludis/syncers/Makefile
@@ -1323,6 +1326,7 @@ AC_OUTPUT(
src/clients/gtkpaludis/vtemm/Makefile
src/clients/gtkpaludis/vtemm/converts/Makefile
src/clients/gtkpaludis/vtemm/defs/Makefile
+ src/clients/importare/Makefile
src/clients/inquisitio/Makefile
src/clients/instruo/Makefile
src/clients/paludis/Makefile
diff --git a/paludis/repositories/unpackaged/Makefile.am b/paludis/repositories/unpackaged/Makefile.am
new file mode 100644
index 0000000..7f337f9
--- /dev/null
+++ b/paludis/repositories/unpackaged/Makefile.am
@@ -0,0 +1,169 @@
+SUBDIRS = .
+CLEANFILES = *~ gmon.out *.gcov *.gcno *.gcda
+DISTCLEANFILES = \
+ ndbam-sr.hh ndbam-sr.cc \
+ ndbam_merger-sr.hh ndbam_merger-sr.cc \
+ ndbam_unmerger-sr.hh ndbam_unmerger-sr.cc \
+ unpackaged_repository-sr.hh unpackaged_repository-sr.cc \
+ installed_unpackaged_repository-sr.hh installed_unpackaged_repository-sr.cc
+
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CXXFLAGS = -I$(top_srcdir) -I$(top_builddir) @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_VISIBILITY@
+DEFS= \
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DLIBEXECDIR=\"$(libexecdir)\" \
+ -DDATADIR=\"$(datadir)\" \
+ -DLIBDIR=\"$(libdir)\"
+
+paludis_repositories_libdir = $(libdir)/paludis/repositories
+paludis_repositories_lib_LTLIBRARIES = libpaludisunpackagedrepository.la
+
+paludis_repositories_unpackaged_includedir = $(includedir)/paludis-$(PALUDIS_PC_SLOT)/paludis/repositories/unpackaged/
+libpaludisunpackagedrepository_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0
+
+paludis_repositories_unpackaged_include_HEADERS = \
+ exceptions.hh \
+ ndbam.hh \
+ ndbam-fwd.hh \
+ ndbam-sr.hh \
+ ndbam_merger.hh \
+ ndbam_merger-sr.hh \
+ ndbam_unmerger.hh \
+ ndbam_unmerger-sr.hh \
+ unpackaged_id.hh \
+ unpackaged_repository.hh \
+ unpackaged_repository-sr.hh \
+ installed_unpackaged_repository.hh \
+ installed_unpackaged_repository-sr.hh \
+ installed_unpackaged_id.hh
+
+libpaludisunpackagedrepository_la_SOURCES = \
+ exceptions.cc \
+ ndbam.cc \
+ ndbam_merger.cc \
+ ndbam_unmerger.cc \
+ unpackaged_id.cc \
+ unpackaged_repository.cc \
+ installed_unpackaged_repository.cc \
+ installed_unpackaged_id.cc \
+ registration.cc \
+ $(paludis_repositories_unpackaged_include_HEADERS)
+
+libpaludisunpackagedrepository_la_LIBADD = \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(DYNAMIC_LD_LIBS)
+
+unpackaged_repository_TEST_SOURCES = unpackaged_repository_TEST.cc
+
+unpackaged_repository_TEST_LDADD = \
+ libpaludisunpackagedrepository.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/util/test_extras.o \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/environments/test/libpaludistestenvironment.la \
+ $(top_builddir)/test/libtest.a \
+ $(DYNAMIC_LD_LIBS)
+
+installed_unpackaged_repository_TEST_SOURCES = installed_unpackaged_repository_TEST.cc
+
+installed_unpackaged_repository_TEST_LDADD = \
+ libpaludisunpackagedrepository.la \
+ $(top_builddir)/paludis/util/libpaludisutil.la \
+ $(top_builddir)/paludis/util/test_extras.o \
+ $(top_builddir)/paludis/libpaludis.la \
+ $(top_builddir)/paludis/environments/test/libpaludistestenvironment.la \
+ $(top_builddir)/test/libtest.a \
+ $(DYNAMIC_LD_LIBS)
+
+EXTRA_DIST = \
+ ndbam.sr \
+ ndbam-sr.hh \
+ ndbam-sr.cc \
+ ndbam_merger.sr \
+ ndbam_merger-sr.hh \
+ ndbam_merger-sr.cc \
+ ndbam_unmerger.sr \
+ ndbam_unmerger-sr.hh \
+ ndbam_unmerger-sr.cc \
+ unpackaged_repository_TEST.cc \
+ unpackaged_repository_TEST_setup.sh \
+ unpackaged_repository_TEST_cleanup.sh \
+ unpackaged_repository.sr \
+ unpackaged_repository-sr.hh \
+ unpackaged_repository-sr.cc \
+ installed_unpackaged_repository_TEST.cc
+ installed_unpackaged_repository_TEST_setup.sh \
+ installed_unpackaged_repository_TEST_cleanup.sh \
+ installed_unpackaged_repository.sr \
+ installed_unpackaged_repository-sr.hh \
+ installed_unpackaged_repository-sr.cc \
+ installed_unpackaged_id.cc
+
+BUILT_SOURCES = \
+ ndbam-sr.hh ndbam-sr.cc \
+ ndbam_merger-sr.hh ndbam_merger-sr.cc \
+ ndbam_unmerger-sr.hh ndbam_unmerger-sr.cc \
+ unpackaged_repository-sr.hh unpackaged_repository-sr.cc \
+ installed_unpackaged_repository-sr.hh installed_unpackaged_repository-sr.cc
+
+check_SCRIPTS = \
+ unpackaged_repository_TEST_setup.sh \
+ unpackaged_repository_TEST_cleanup.sh \
+ installed_unpackaged_repository_TEST_setup.sh \
+ installed_unpackaged_repository_TEST_cleanup.sh
+
+TESTS_ENVIRONMENT = env \
+ TEST_OUTPUT_WRAPPER="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/outputwrapper`" \
+ PALUDIS_OUTPUTWRAPPER_DIR="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/util/`" \
+ PALUDIS_EBUILD_DIR="`$(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_srcdir)/paludis/repositories/e/ebuild/`" \
+ PALUDIS_EAPIS_DIR="$(top_srcdir)/paludis/repositories/e/eapis/" \
+ PALUDIS_DISTRIBUTIONS_DIR="$(top_srcdir)/paludis/distributions/" \
+ PALUDIS_DISTRIBUTION="gentoo" \
+ PALUDIS_FETCHERS_DIR="$(top_srcdir)/paludis/fetchers/" \
+ PALUDIS_SKIP_CONFIG="yes" \
+ TEST_SCRIPT_DIR="$(srcdir)/" \
+ PALUDIS_REPOSITORY_SO_DIR="$(top_builddir)/paludis/repositories" \
+ PALUDIS_NO_CHOWN="yes" \
+ bash $(top_srcdir)/test/run_test.sh
+
+TESTS = \
+ unpackaged_repository_TEST \
+ installed_unpackaged_repository_TEST
+
+check_PROGRAMS = $(TESTS)
+
+built-sources : $(BUILT_SOURCES)
+ for s in `echo $(SUBDIRS) | tr -d .` ; do $(MAKE) -C $$s built-sources || exit 1 ; done
+
+ndbam-sr.hh : ndbam.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --header $(srcdir)/ndbam.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+ndbam-sr.cc : ndbam.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --source $(srcdir)/ndbam.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+ndbam_merger-sr.hh : ndbam_merger.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --header $(srcdir)/ndbam_merger.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+ndbam_merger-sr.cc : ndbam_merger.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --source $(srcdir)/ndbam_merger.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+ndbam_unmerger-sr.hh : ndbam_unmerger.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --header $(srcdir)/ndbam_unmerger.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+ndbam_unmerger-sr.cc : ndbam_unmerger.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --source $(srcdir)/ndbam_unmerger.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+unpackaged_repository-sr.hh : unpackaged_repository.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --header $(srcdir)/unpackaged_repository.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+unpackaged_repository-sr.cc : unpackaged_repository.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --source $(srcdir)/unpackaged_repository.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+installed_unpackaged_repository-sr.hh : installed_unpackaged_repository.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --header $(srcdir)/installed_unpackaged_repository.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
+installed_unpackaged_repository-sr.cc : installed_unpackaged_repository.sr $(top_srcdir)/misc/make_sr.bash
+ if ! $(top_srcdir)/misc/make_sr.bash --source $(srcdir)/installed_unpackaged_repository.sr > $@ ; then rm -f $@ ; exit 1 ; fi
+
diff --git a/paludis/repositories/unpackaged/exceptions.cc b/paludis/repositories/unpackaged/exceptions.cc
new file mode 100644
index 0000000..4258e02
--- /dev/null
+++ b/paludis/repositories/unpackaged/exceptions.cc
@@ -0,0 +1,29 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 "exceptions.hh"
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+RepositoryConfigurationError::RepositoryConfigurationError(const std::string & s) throw () :
+ ConfigurationError(s)
+{
+}
+
diff --git a/paludis/repositories/unpackaged/exceptions.hh b/paludis/repositories/unpackaged/exceptions.hh
new file mode 100644
index 0000000..dd7069e
--- /dev/null
+++ b/paludis/repositories/unpackaged/exceptions.hh
@@ -0,0 +1,38 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_EXCEPTIONS_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_EXCEPTIONS_HH 1
+
+#include <paludis/util/exception.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+ class PALUDIS_VISIBLE RepositoryConfigurationError :
+ public ConfigurationError
+ {
+ public:
+ RepositoryConfigurationError(const std::string & msg) throw ();
+ };
+ }
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_id.cc b/paludis/repositories/unpackaged/installed_unpackaged_id.cc
new file mode 100644
index 0000000..e5f40e3
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_id.cc
@@ -0,0 +1,589 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/installed_unpackaged_id.hh>
+#include <paludis/repositories/unpackaged/installed_unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/ndbam.hh>
+#include <paludis/repositories/unpackaged/ndbam_unmerger.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/system.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/visitor-impl.hh>
+#include <paludis/util/visitor_cast.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/strip.hh>
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/name.hh>
+#include <paludis/version_spec.hh>
+#include <paludis/package_database.hh>
+#include <paludis/contents.hh>
+#include <paludis/environment.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/action.hh>
+#include <paludis/hashed_containers.hh>
+#include <fstream>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+namespace
+{
+ class InstalledUnpackagedFSEntryKey :
+ public MetadataFSEntryKey
+ {
+ private:
+ const FSEntry _location;
+
+ public:
+ InstalledUnpackagedFSEntryKey(const FSEntry & l) :
+ MetadataFSEntryKey("location", "Location", mkt_internal),
+ _location(l)
+ {
+ }
+
+ const FSEntry value() const
+ {
+ return _location;
+ }
+ };
+
+ void create_file(Contents & c, const FSEntry & f)
+ {
+ c.add(make_shared_ptr(new ContentsFileEntry(stringify(f))));
+ }
+
+ void create_dir(Contents & c, const FSEntry & f)
+ {
+ c.add(make_shared_ptr(new ContentsDirEntry(stringify(f))));
+ }
+
+ void create_sym(Contents & c, const FSEntry & f, const FSEntry & t)
+ {
+ c.add(make_shared_ptr(new ContentsSymEntry(stringify(f), stringify(t))));
+ }
+
+ class InstalledUnpackagedContentsKey :
+ public MetadataContentsKey
+ {
+ private:
+ const PackageID * const _id;
+ const NDBAM * const _db;
+ mutable Mutex _mutex;
+ mutable tr1::shared_ptr<Contents> _v;
+
+ public:
+ InstalledUnpackagedContentsKey(const PackageID * const i, const NDBAM * const d) :
+ MetadataContentsKey("contents", "Contents", mkt_internal),
+ _id(i),
+ _db(d)
+ {
+ }
+
+ const tr1::shared_ptr<const Contents> value() const
+ {
+ Lock l(_mutex);
+ if (_v)
+ return _v;
+
+ using namespace tr1::placeholders;
+ _v.reset(new Contents);
+ _db->parse_contents(*_id,
+ tr1::bind(&create_file, tr1::ref(*_v), _1),
+ tr1::bind(&create_dir, tr1::ref(*_v), _1),
+ tr1::bind(&create_sym, tr1::ref(*_v), _1, _2)
+ );
+ return _v;
+ }
+ };
+
+ class InstalledUnpackagedTimeKey :
+ public MetadataTimeKey
+ {
+ private:
+ const time_t _time;
+
+ public:
+ InstalledUnpackagedTimeKey(const FSEntry & f) :
+ MetadataTimeKey("installed_time", "Installed time", mkt_normal),
+ _time(f.mtime())
+ {
+ }
+
+ const time_t value() const
+ {
+ return _time;
+ }
+ };
+
+ class InstalledUnpackagedStringKey :
+ public MetadataStringKey
+ {
+ private:
+ mutable tr1::shared_ptr<const std::string> _v;
+ mutable Mutex _mutex;
+ const FSEntry _f;
+
+ public:
+ InstalledUnpackagedStringKey(const std::string & r, const std::string & h, const FSEntry & f) :
+ MetadataStringKey(r, h, mkt_normal),
+ _f(f)
+ {
+ }
+
+ const std::string value() const
+ {
+ Lock l(_mutex);
+ if (_v)
+ return *_v;
+ std::ifstream f(stringify(_f).c_str());
+ if (! f)
+ {
+ Context context("When reading '" + stringify(_f) + "' as an InstalledUnpackagedStringKey:");
+ throw FSError("Couldn't open '" + stringify(_f) + "' for read");
+ }
+ _v.reset(new std::string(
+ strip_trailing(std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>()), "\n")));
+ return *_v;
+ }
+ };
+}
+
+namespace paludis
+{
+ template <>
+ struct Implementation<InstalledUnpackagedID>
+ {
+ const Environment * const env;
+ const QualifiedPackageName name;
+ const VersionSpec version;
+ const SlotName slot;
+ const RepositoryName repository_name;
+ const FSEntry root;
+ const NDBAM * const ndbam;
+
+ tr1::shared_ptr<InstalledUnpackagedFSEntryKey> fs_location_key;
+ tr1::shared_ptr<InstalledUnpackagedContentsKey> contents_key;
+ tr1::shared_ptr<InstalledUnpackagedTimeKey> installed_time_key;
+ tr1::shared_ptr<InstalledUnpackagedStringKey> source_origin_key;
+ tr1::shared_ptr<InstalledUnpackagedStringKey> binary_origin_key;
+
+ Implementation(
+ const Environment * const e,
+ const PackageID * const id,
+ const QualifiedPackageName & q,
+ const VersionSpec & v,
+ const SlotName & s,
+ const RepositoryName & r,
+ const FSEntry & l,
+ const FSEntry & ro,
+ const NDBAM * const d) :
+ env(e),
+ name(q),
+ version(v),
+ slot(s),
+ repository_name(r),
+ root(ro),
+ ndbam(d),
+ fs_location_key(new InstalledUnpackagedFSEntryKey(l))
+ {
+ if ((l / "contents").exists())
+ {
+ contents_key.reset(new InstalledUnpackagedContentsKey(id, d));
+ installed_time_key.reset(new InstalledUnpackagedTimeKey(l / "contents"));
+ }
+
+ if ((l / "source_repository").exists())
+ source_origin_key.reset(new InstalledUnpackagedStringKey("source_repository", "Source repository", l / "source_repository"));
+
+ if ((l / "binary_repository").exists())
+ binary_origin_key.reset(new InstalledUnpackagedStringKey("binary_repository", "Binary repository", l / "binary_repository"));
+ }
+ };
+}
+
+InstalledUnpackagedID::InstalledUnpackagedID(const Environment * const e, const QualifiedPackageName & q,
+ const VersionSpec & v, const SlotName & s, const RepositoryName & n, const FSEntry & l,
+ const std::string &, const FSEntry & ro, const NDBAM * const d) :
+ PrivateImplementationPattern<InstalledUnpackagedID>(new Implementation<InstalledUnpackagedID>(e, this, q, v, s, n, l, ro, d)),
+ _imp(PrivateImplementationPattern<InstalledUnpackagedID>::_imp.get())
+{
+ add_metadata_key(_imp->fs_location_key);
+ if (_imp->contents_key)
+ add_metadata_key(_imp->contents_key);
+ if (_imp->installed_time_key)
+ add_metadata_key(_imp->installed_time_key);
+ if (_imp->source_origin_key)
+ add_metadata_key(_imp->source_origin_key);
+ if (_imp->binary_origin_key)
+ add_metadata_key(_imp->binary_origin_key);
+}
+
+InstalledUnpackagedID::~InstalledUnpackagedID()
+{
+}
+
+void
+InstalledUnpackagedID::need_keys_added() const
+{
+}
+
+void
+InstalledUnpackagedID::need_masks_added() const
+{
+}
+
+const std::string
+InstalledUnpackagedID::canonical_form(const PackageIDCanonicalForm f) const
+{
+ switch (f)
+ {
+ case idcf_full:
+ return stringify(_imp->name) + "-" + stringify(_imp->version) + ":" +
+ stringify(slot()) + "::" + stringify(_imp->repository_name);
+
+ case idcf_version:
+ return stringify(_imp->version);
+
+ case idcf_no_version:
+ return stringify(_imp->name) + ":" + stringify(slot()) + "::" +
+ stringify(_imp->repository_name);
+
+ case last_idcf:
+ break;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Bad PackageIDCanonicalForm");
+}
+
+const QualifiedPackageName
+InstalledUnpackagedID::name() const
+{
+ return _imp->name;
+}
+
+const VersionSpec
+InstalledUnpackagedID::version() const
+{
+ return _imp->version;
+}
+
+const SlotName
+InstalledUnpackagedID::slot() const
+{
+ return _imp->slot;
+}
+
+const tr1::shared_ptr<const Repository>
+InstalledUnpackagedID::repository() const
+{
+ return _imp->env->package_database()->fetch_repository(_imp->repository_name);
+}
+
+const tr1::shared_ptr<const MetadataPackageIDKey>
+InstalledUnpackagedID::virtual_for_key() const
+{
+ return tr1::shared_ptr<const MetadataPackageIDKey>();
+}
+
+const tr1::shared_ptr<const MetadataSetKey<KeywordNameSet> >
+InstalledUnpackagedID::keywords_key() const
+{
+ return tr1::shared_ptr<const MetadataSetKey<KeywordNameSet> >();
+}
+
+const tr1::shared_ptr<const MetadataSetKey<IUseFlagSet> >
+InstalledUnpackagedID::iuse_key() const
+{
+ return tr1::shared_ptr<const MetadataSetKey<IUseFlagSet> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<ProvideSpecTree> >
+InstalledUnpackagedID::provide_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<ProvideSpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSetKey<PackageIDSequence> >
+InstalledUnpackagedID::contains_key() const
+{
+ return tr1::shared_ptr<const MetadataSetKey<PackageIDSequence> >();
+}
+
+const tr1::shared_ptr<const MetadataPackageIDKey>
+InstalledUnpackagedID::contained_in_key() const
+{
+ return tr1::shared_ptr<const MetadataPackageIDKey>();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+InstalledUnpackagedID::build_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+InstalledUnpackagedID::run_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+InstalledUnpackagedID::post_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+InstalledUnpackagedID::suggested_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> >
+InstalledUnpackagedID::fetches_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> >
+InstalledUnpackagedID::homepage_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+InstalledUnpackagedID::short_description_key() const
+{
+ return tr1::shared_ptr<const MetadataStringKey>();
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+InstalledUnpackagedID::long_description_key() const
+{
+ return tr1::shared_ptr<const MetadataStringKey>();
+}
+
+const tr1::shared_ptr<const MetadataContentsKey>
+InstalledUnpackagedID::contents_key() const
+{
+ return _imp->contents_key;
+}
+
+const tr1::shared_ptr<const MetadataTimeKey>
+InstalledUnpackagedID::installed_time_key() const
+{
+ return _imp->installed_time_key;
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+InstalledUnpackagedID::source_origin_key() const
+{
+ return _imp->source_origin_key;
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+InstalledUnpackagedID::binary_origin_key() const
+{
+ return _imp->binary_origin_key;
+}
+
+const tr1::shared_ptr<const MetadataFSEntryKey>
+InstalledUnpackagedID::fs_location_key() const
+{
+ return _imp->fs_location_key;
+}
+
+namespace
+{
+ struct SupportVisitor :
+ ConstVisitor<SupportsActionTestVisitorTypes>
+ {
+ bool result;
+
+ void visit(const SupportsActionTest<UninstallAction> &)
+ {
+ result = true;
+ }
+
+ void visit(const SupportsActionTest<InstalledAction> &)
+ {
+ result = true;
+ }
+
+ void visit(const SupportsActionTest<ConfigAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<InfoAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<PretendAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<FetchAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<InstallAction> &)
+ {
+ result = false;
+ }
+ };
+
+ struct PerformAction :
+ Visitor<ActionVisitorTypes>
+ {
+ const InstalledUnpackagedID * const id;
+
+ PerformAction(const InstalledUnpackagedID * const i) :
+ id(i)
+ {
+ }
+
+ void visit(InstallAction & a) PALUDIS_ATTRIBUTE((noreturn))
+ {
+ throw UnsupportedActionError(*id, a);
+ }
+
+ void visit(FetchAction & a) PALUDIS_ATTRIBUTE((noreturn))
+ {
+ throw UnsupportedActionError(*id, a);
+ }
+
+ void visit(ConfigAction & a) PALUDIS_ATTRIBUTE((noreturn))
+ {
+ throw UnsupportedActionError(*id, a);
+ }
+
+ void visit(PretendAction & a) PALUDIS_ATTRIBUTE((noreturn))
+ {
+ throw UnsupportedActionError(*id, a);
+ }
+
+ void visit(InfoAction & a) PALUDIS_ATTRIBUTE((noreturn))
+ {
+ throw UnsupportedActionError(*id, a);
+ }
+
+ void visit(InstalledAction &)
+ {
+ }
+
+ void visit(UninstallAction & a)
+ {
+ id->uninstall(a.options, false);
+ }
+ };
+}
+
+bool
+InstalledUnpackagedID::supports_action(const SupportsActionTestBase & test) const
+{
+ SupportVisitor v;
+ test.accept(v);
+ return v.result;
+}
+
+void
+InstalledUnpackagedID::perform_action(Action & action) const
+{
+ PerformAction v(this);
+ action.accept(v);
+}
+
+void
+InstalledUnpackagedID::invalidate_masks() const
+{
+}
+
+bool
+InstalledUnpackagedID::breaks_portage() const
+{
+ return true;
+}
+
+bool
+InstalledUnpackagedID::arbitrary_less_than_comparison(const PackageID & other) const
+{
+ return slot().data() < other.slot().data();
+}
+
+std::size_t
+InstalledUnpackagedID::extra_hash_value() const
+{
+ return CRCHash<SlotName>()(slot());
+}
+
+void
+InstalledUnpackagedID::uninstall(const UninstallActionOptions &, const bool replace) const
+{
+ Context context("When uninstalling '" + stringify(*this) + "':");
+
+ bool last(! replace);
+ if (last)
+ {
+ tr1::shared_ptr<const PackageIDSequence> ids(repository()->package_ids(name()));
+ for (PackageIDSequence::ConstIterator v(ids->begin()), v_end(ids->end()) ;
+ v != v_end ; ++v)
+ if (**v != *this)
+ {
+ last = false;
+ break;
+ }
+ }
+
+ if (! _imp->root.is_directory())
+ throw InstallActionError("Couldn't uninstall '" + stringify(*this) +
+ "' because root ('" + stringify(_imp->root) + "') is not a directory");
+
+ FSEntry ver_dir(fs_location_key()->value());
+
+ NDBAMUnmerger unmerger(
+ NDBAMUnmergerOptions::create()
+ .environment(_imp->env)
+ .root(_imp->root)
+ .contents_file(ver_dir / "contents")
+ .config_protect(getenv_with_default("CONFIG_PROTECT", ""))
+ .config_protect_mask(getenv_with_default("CONFIG_PROTECT_MASK", ""))
+ .ndbam(_imp->ndbam)
+ .package_id(shared_from_this()));
+
+ unmerger.unmerge();
+
+ for (DirIterator d(ver_dir, false), d_end ; d != d_end ; ++d)
+ FSEntry(*d).unlink();
+ ver_dir.rmdir();
+
+ if (last)
+ {
+ FSEntry pkg_dir(fs_location_key()->value().dirname());
+ pkg_dir.rmdir();
+
+ tr1::static_pointer_cast<const InstalledUnpackagedRepository>(repository())->deindex(name());
+ }
+}
+
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_id.hh b/paludis/repositories/unpackaged/installed_unpackaged_id.hh
new file mode 100644
index 0000000..8c435d5
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_id.hh
@@ -0,0 +1,95 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_INSTALLED_UNPACKAGED_ID_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_INSTALLED_UNPACKAGED_ID_HH 1
+
+#include <paludis/package_id.hh>
+#include <paludis/name-fwd.hh>
+#include <paludis/action-fwd.hh>
+#include <paludis/util/fs_entry-fwd.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/tr1_memory.hh>
+#include <paludis/repositories/unpackaged/ndbam-fwd.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+ class InstalledUnpackagedID :
+ public PackageID,
+ private PrivateImplementationPattern<InstalledUnpackagedID>,
+ public tr1::enable_shared_from_this<InstalledUnpackagedID>
+ {
+ private:
+ Implementation<InstalledUnpackagedID> * const _imp;
+
+ protected:
+ void need_keys_added() const;
+ void need_masks_added() const;
+
+ public:
+ InstalledUnpackagedID(const Environment * const, const QualifiedPackageName &, const VersionSpec &,
+ const SlotName &, const RepositoryName &, const FSEntry &, const std::string &, const FSEntry &,
+ const NDBAM * const);
+
+ ~InstalledUnpackagedID();
+
+ virtual const std::string canonical_form(const PackageIDCanonicalForm) const;
+ virtual const QualifiedPackageName name() const;
+ virtual const VersionSpec version() const;
+ virtual const SlotName slot() const;
+ virtual const tr1::shared_ptr<const Repository> repository() const;
+
+ virtual const tr1::shared_ptr<const MetadataPackageIDKey> virtual_for_key() const;
+ virtual const tr1::shared_ptr<const MetadataSetKey<KeywordNameSet> > keywords_key() const;
+ virtual const tr1::shared_ptr<const MetadataSetKey<IUseFlagSet> > iuse_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<ProvideSpecTree> > provide_key() const;
+ virtual const tr1::shared_ptr<const MetadataSetKey<PackageIDSequence> > contains_key() const;
+ virtual const tr1::shared_ptr<const MetadataPackageIDKey> contained_in_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > build_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > run_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > post_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > suggested_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> > fetches_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> > homepage_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> short_description_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> long_description_key() const;
+ virtual const tr1::shared_ptr<const MetadataContentsKey> contents_key() const;
+ virtual const tr1::shared_ptr<const MetadataTimeKey> installed_time_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> source_origin_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> binary_origin_key() const;
+ virtual const tr1::shared_ptr<const MetadataFSEntryKey> fs_location_key() const;
+
+ virtual bool supports_action(const SupportsActionTestBase &) const PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual void perform_action(Action &) const;
+
+ virtual void invalidate_masks() const;
+ virtual bool breaks_portage() const PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual bool arbitrary_less_than_comparison(const PackageID &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual std::size_t extra_hash_value() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ void uninstall(const UninstallActionOptions &, const bool replace) const;
+ };
+ }
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_repository.cc b/paludis/repositories/unpackaged/installed_unpackaged_repository.cc
new file mode 100644
index 0000000..a7e2cd4
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_repository.cc
@@ -0,0 +1,318 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/installed_unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/installed_unpackaged_id.hh>
+#include <paludis/repositories/unpackaged/ndbam.hh>
+#include <paludis/repositories/unpackaged/ndbam_merger.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/iterator.hh>
+#include <paludis/util/visitor-impl.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/system.hh>
+#include <paludis/action.hh>
+#include <paludis/environment.hh>
+#include <paludis/metadata_key.hh>
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+#include <fstream>
+#include <sstream>
+#include <sys/time.h>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+#include <paludis/repositories/unpackaged/installed_unpackaged_repository-sr.cc>
+
+namespace paludis
+{
+ template <>
+ struct Implementation<InstalledUnpackagedRepository>
+ {
+ const InstalledUnpackagedRepositoryParams params;
+ mutable NDBAM ndbam;
+
+ Implementation(const InstalledUnpackagedRepositoryParams & p) :
+ params(p),
+ ndbam(p.location)
+ {
+ }
+ };
+}
+
+InstalledUnpackagedRepository::InstalledUnpackagedRepository(
+ const RepositoryName & n, const InstalledUnpackagedRepositoryParams & p) :
+ PrivateImplementationPattern<InstalledUnpackagedRepository>(new Implementation<InstalledUnpackagedRepository>(p)),
+ Repository(n, RepositoryCapabilities::create()
+ .installed_interface(this)
+ .sets_interface(0)
+ .syncable_interface(0)
+ .use_interface(0)
+ .world_interface(0)
+ .mirrors_interface(0)
+ .environment_variable_interface(0)
+ .provides_interface(0)
+ .virtuals_interface(0)
+ .make_virtuals_interface(0)
+ .destination_interface(this)
+ .licenses_interface(0)
+ .e_interface(0)
+ .hook_interface(0)
+ .qa_interface(0)
+ .manifest_interface(0),
+ "installed-unpackaged")
+{
+}
+
+InstalledUnpackagedRepository::~InstalledUnpackagedRepository()
+{
+}
+
+tr1::shared_ptr<const PackageIDSequence>
+InstalledUnpackagedRepository::do_package_ids(const QualifiedPackageName & q) const
+{
+ tr1::shared_ptr<NDBAMEntrySequence> entries(_imp->ndbam.entries(q));
+ tr1::shared_ptr<PackageIDSequence> result(new PackageIDSequence);
+
+ for (IndirectIterator<NDBAMEntrySequence::ConstIterator> e(entries->begin()), e_end(entries->end()) ;
+ e != e_end ; ++e)
+ {
+ Lock l(*e->mutex);
+ if (! e->package_id)
+ e->package_id.reset(new InstalledUnpackagedID(_imp->params.environment, e->name, e->version,
+ e->slot, name(), e->fs_location, e->magic, root(), &_imp->ndbam));
+ result->push_back(e->package_id);
+ }
+
+ return result;
+}
+
+tr1::shared_ptr<const QualifiedPackageNameSet>
+InstalledUnpackagedRepository::do_package_names(const CategoryNamePart & c) const
+{
+ return _imp->ndbam.package_names(c);
+}
+
+tr1::shared_ptr<const CategoryNamePartSet>
+InstalledUnpackagedRepository::do_category_names() const
+{
+ return _imp->ndbam.category_names();
+}
+
+tr1::shared_ptr<const CategoryNamePartSet>
+InstalledUnpackagedRepository::do_category_names_containing_package(
+ const PackageNamePart & p) const
+{
+ return _imp->ndbam.category_names_containing_package(p);
+}
+
+bool
+InstalledUnpackagedRepository::do_has_package_named(const QualifiedPackageName & q) const
+{
+ return _imp->ndbam.has_package_named(q);
+}
+
+bool
+InstalledUnpackagedRepository::do_has_category_named(const CategoryNamePart & c) const
+{
+ return _imp->ndbam.has_category_named(c);
+}
+
+namespace
+{
+ struct SomeIDsMightSupportVisitor :
+ ConstVisitor<SupportsActionTestVisitorTypes>
+ {
+ bool result;
+
+ void visit(const SupportsActionTest<UninstallAction> &)
+ {
+ result = true;
+ }
+
+ void visit(const SupportsActionTest<InstalledAction> &)
+ {
+ result = true;
+ }
+
+ void visit(const SupportsActionTest<ConfigAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<InfoAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<PretendAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<FetchAction> &)
+ {
+ result = false;
+ }
+
+ void visit(const SupportsActionTest<InstallAction> &)
+ {
+ result = false;
+ }
+ };
+}
+
+bool
+InstalledUnpackagedRepository::do_some_ids_might_support_action(const SupportsActionTestBase & test) const
+{
+ SomeIDsMightSupportVisitor v;
+ test.accept(v);
+ return v.result;
+}
+
+void
+InstalledUnpackagedRepository::merge(const MergeOptions & m)
+{
+ Context context("When merging '" + stringify(*m.package_id) + "' at '" + stringify(m.image_dir)
+ + "' to InstalledUnpackagedRepository repository '" + stringify(name()) + "':");
+
+ if (! is_suitable_destination_for(*m.package_id))
+ throw InstallActionError("Not a suitable destination for '" + stringify(*m.package_id) + "'");
+
+ tr1::shared_ptr<const PackageID> if_overwritten_id, if_same_name_id;
+ {
+ tr1::shared_ptr<const PackageIDSequence> ids(package_ids(m.package_id->name()));
+ for (PackageIDSequence::ConstIterator v(ids->begin()), v_end(ids->end()) ;
+ v != v_end ; ++v)
+ {
+ if_same_name_id = *v;
+ if ((*v)->version() == m.package_id->version() && (*v)->slot() == m.package_id->slot())
+ {
+ if_overwritten_id = *v;
+ break;
+ }
+ }
+ }
+
+ FSEntry uid_dir(_imp->params.location);
+ if (if_same_name_id)
+ uid_dir = if_same_name_id->fs_location_key()->value().dirname();
+ else
+ {
+ std::string uid(stringify(m.package_id->name().category) + "---" + stringify(m.package_id->name().package));
+ uid_dir /= "data";
+ uid_dir.mkdir();
+ uid_dir /= uid;
+ uid_dir.mkdir();
+ }
+
+ FSEntry target_ver_dir(uid_dir);
+ {
+ struct timeval t;
+ gettimeofday(&t, 0);
+ std::ostringstream magic;
+ magic << std::hex << t.tv_usec << "x" << t.tv_sec;
+ target_ver_dir /= (stringify(m.package_id->version()) + ":" + stringify(m.package_id->slot()) + ":" + magic.str());
+ }
+
+ if (target_ver_dir.exists())
+ throw InstallActionError("Temporary merge directory '" + stringify(target_ver_dir) + "' already exists, probably "
+ "due to a previous failed install. If it is safe to do so, please remove this directory and try again.");
+ target_ver_dir.mkdir();
+
+ {
+ std::ofstream source_repository_file(stringify(target_ver_dir / "source_repository").c_str());
+ source_repository_file << m.package_id->repository()->name() << std::endl;
+ if (! source_repository_file)
+ throw InstallActionError("Could not write to '" + stringify(target_ver_dir / "source_repository") + "'");
+ }
+
+ NDBAMMerger merger(
+ NDBAMMergerOptions::create()
+ .environment(_imp->params.environment)
+ .image(m.image_dir)
+ .root(root())
+ .contents_file(target_ver_dir / "contents")
+ .config_protect(getenv_with_default("CONFIG_PROTECT", ""))
+ .config_protect_mask(getenv_with_default("CONFIG_PROTECT_MASK", ""))
+ .package_id(m.package_id));
+
+ if (! merger.check())
+ {
+ for (DirIterator d(target_ver_dir, false), d_end ; d != d_end ; ++d)
+ FSEntry(*d).unlink();
+ target_ver_dir.rmdir();
+ throw InstallActionError("Not proceeding with install due to merge sanity check failing");
+ }
+
+ merger.merge();
+
+ _imp->ndbam.index(m.package_id->name(), uid_dir.basename());
+
+ if (if_overwritten_id)
+ {
+ tr1::static_pointer_cast<const InstalledUnpackagedID>(if_overwritten_id)->uninstall(UninstallActionOptions::create()
+ .no_config_protect(false),
+ true);
+ }
+}
+
+bool
+InstalledUnpackagedRepository::is_suitable_destination_for(const PackageID & e) const
+{
+ std::string f(e.repository()->format());
+ return f == "unpackaged";
+}
+
+bool
+InstalledUnpackagedRepository::is_default_destination() const
+{
+ return _imp->params.environment->root() == root();
+}
+
+bool
+InstalledUnpackagedRepository::want_pre_post_phases() const
+{
+ return true;
+}
+
+FSEntry
+InstalledUnpackagedRepository::root() const
+{
+ return _imp->params.root;
+}
+
+void
+InstalledUnpackagedRepository::invalidate()
+{
+ _imp.reset(new Implementation<InstalledUnpackagedRepository>(_imp->params));
+}
+
+void
+InstalledUnpackagedRepository::invalidate_masks()
+{
+}
+
+void
+InstalledUnpackagedRepository::deindex(const QualifiedPackageName & q) const
+{
+ _imp->ndbam.deindex(q);
+}
+
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_repository.hh b/paludis/repositories/unpackaged/installed_unpackaged_repository.hh
new file mode 100644
index 0000000..2f1014f
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_repository.hh
@@ -0,0 +1,92 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_INSTALLED_UNPACKAGED_REPOSITORY_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_INSTALLED_UNPACKAGED_REPOSITORY_HH 1
+
+#include <paludis/repository.hh>
+#include <paludis/util/map.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+#include <paludis/repositories/unpackaged/installed_unpackaged_repository-sr.hh>
+ }
+
+ class PALUDIS_VISIBLE InstalledUnpackagedRepository :
+ private PrivateImplementationPattern<InstalledUnpackagedRepository>,
+ public Repository,
+ public RepositoryDestinationInterface,
+ public RepositoryInstalledInterface
+ {
+ protected:
+ virtual tr1::shared_ptr<const PackageIDSequence> do_package_ids(
+ const QualifiedPackageName &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual tr1::shared_ptr<const QualifiedPackageNameSet> do_package_names(
+ const CategoryNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual tr1::shared_ptr<const CategoryNamePartSet> do_category_names() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual tr1::shared_ptr<const CategoryNamePartSet> do_category_names_containing_package(
+ const PackageNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool do_has_package_named(const QualifiedPackageName &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool do_has_category_named(const CategoryNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool do_some_ids_might_support_action(const SupportsActionTestBase &) const;
+
+ public:
+ InstalledUnpackagedRepository(
+ const RepositoryName &,
+ const unpackaged_repositories::InstalledUnpackagedRepositoryParams &);
+
+ ~InstalledUnpackagedRepository();
+
+ virtual void invalidate();
+ virtual void invalidate_masks();
+
+ virtual bool is_suitable_destination_for(const PackageID &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool is_default_destination() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool want_pre_post_phases() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual void merge(const MergeOptions &);
+
+ virtual FSEntry root() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ void deindex(const QualifiedPackageName &) const;
+ };
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_repository.sr b/paludis/repositories/unpackaged/installed_unpackaged_repository.sr
new file mode 100644
index 0000000..246cecc
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_repository.sr
@@ -0,0 +1,14 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et :
+
+make_class_InstalledUnpackagedRepositoryParams()
+{
+ visible
+
+ key environment "Environment *"
+ key location FSEntry
+ key root FSEntry
+
+ allow_named_args
+}
+
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST.cc b/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST.cc
new file mode 100644
index 0000000..7e55af9
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST.cc
@@ -0,0 +1,596 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/installed_unpackaged_repository.hh>
+#include <paludis/environments/test/test_environment.hh>
+#include <paludis/package_database.hh>
+#include <paludis/query.hh>
+#include <paludis/package_id.hh>
+#include <paludis/action.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/util/iterator.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/iterator.hh>
+#include <paludis/util/visitor-impl.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+#include <algorithm>
+#include <sstream>
+
+using namespace test;
+using namespace paludis;
+
+namespace
+{
+ struct ContentsDumper :
+ ConstVisitor<ContentsVisitorTypes>
+ {
+ std::stringstream s;
+
+ void visit(const ContentsFileEntry & f)
+ {
+ s << "file<" << f.name() << ">";
+ }
+
+ void visit(const ContentsDirEntry & f)
+ {
+ s << "dir<" << f.name() << ">";
+ }
+
+ void visit(const ContentsSymEntry & f)
+ {
+ s << "sym<" << f.name() << "=" << f.target() << ">";
+ }
+
+ void visit(const ContentsMiscEntry & f)
+ {
+ s << "misc<" << f.name() << ">";
+ }
+
+ void visit(const ContentsDevEntry & f)
+ {
+ s << "dev<" << f.name() << ">";
+ }
+
+ void visit(const ContentsFifoEntry & f)
+ {
+ s << "fifo<" << f.name() << ">";
+ }
+ };
+}
+
+namespace test_cases
+{
+ struct MembersTest : TestCase
+ {
+ MembersTest() : TestCase("members") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo1"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageIDSequence> ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(ids->begin()), indirect_iterator(ids->end()), " "),
+ "cat-one/foo-1:0::installed-unpackaged cat-one/foo-2:1::installed-unpackaged");
+ }
+ } test_members;
+
+ struct MetadataTest : TestCase
+ {
+ MetadataTest() : TestCase("metadata") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo1"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageID> id1(
+ *env.package_database()->query(query::Matches(PackageDepSpec("cat-one/foo:0", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin());
+
+ TEST_CHECK_EQUAL(id1->version(), VersionSpec("1"));
+ TEST_CHECK_EQUAL(id1->slot(), SlotName("0"));
+ TEST_CHECK_EQUAL(id1->name(), QualifiedPackageName("cat-one/foo"));
+ TEST_CHECK_EQUAL(id1->repository()->name(), RepositoryName("installed-unpackaged"));
+ TEST_CHECK(id1->fs_location_key());
+ TEST_CHECK_EQUAL(id1->fs_location_key()->value(),
+ FSEntry::cwd() / "installed_unpackaged_repository_TEST_dir/repo1/data/giant-space-weasel/1:0:foo/");
+
+ TEST_CHECK(id1->contents_key());
+ ContentsDumper d1;
+ std::for_each(indirect_iterator(id1->contents_key()->value()->begin()),
+ indirect_iterator(id1->contents_key()->value()->end()), accept_visitor(d1));
+ TEST_CHECK_EQUAL(d1.s.str(), "dir</fnord>");
+
+ const tr1::shared_ptr<const PackageID> id2(
+ *env.package_database()->query(query::Matches(PackageDepSpec("cat-one/foo:1", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin());
+
+ TEST_CHECK_EQUAL(id2->version(), VersionSpec("2"));
+ TEST_CHECK_EQUAL(id2->slot(), SlotName("1"));
+ TEST_CHECK_EQUAL(id2->name(), QualifiedPackageName("cat-one/foo"));
+ TEST_CHECK_EQUAL(id2->repository()->name(), RepositoryName("installed-unpackaged"));
+ TEST_CHECK(id2->fs_location_key());
+ TEST_CHECK_EQUAL(id2->fs_location_key()->value(),
+ FSEntry::cwd() / "installed_unpackaged_repository_TEST_dir/repo1/data/giant-space-weasel/2:1:bar/");
+
+ TEST_CHECK(id2->contents_key());
+ ContentsDumper d2;
+ std::for_each(indirect_iterator(id2->contents_key()->value()->begin()),
+ indirect_iterator(id2->contents_key()->value()->end()), accept_visitor(d2));
+ TEST_CHECK_EQUAL(d2.s.str(), "dir</stilton>file</stilton/cheese>file</stilton/is delicious>");
+ }
+ } test_metadata;
+
+ struct MasksTest : TestCase
+ {
+ MasksTest() : TestCase("masks") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo1"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageID> id1(
+ *env.package_database()->query(query::Matches(PackageDepSpec("cat-one/foo:0", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin());
+
+ TEST_CHECK(! id1->masked());
+
+ const tr1::shared_ptr<const PackageID> id2(
+ *env.package_database()->query(query::Matches(PackageDepSpec("cat-one/foo:1", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin());
+
+ TEST_CHECK(! id2->masked());
+ }
+ } test_masks;
+
+ struct ActionsTest : TestCase
+ {
+ ActionsTest() : TestCase("actions") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo1"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<InstallAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<ConfigAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<PretendAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<InfoAction>()));
+ TEST_CHECK(repo->some_ids_might_support_action(SupportsActionTest<UninstallAction>()));
+ TEST_CHECK(repo->some_ids_might_support_action(SupportsActionTest<InstalledAction>()));
+
+ const tr1::shared_ptr<const PackageID> id1(
+ *env.package_database()->query(query::Matches(PackageDepSpec("cat-one/foo:1", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin());
+
+ TEST_CHECK(! id1->supports_action(SupportsActionTest<InstallAction>()));
+ TEST_CHECK(! id1->supports_action(SupportsActionTest<ConfigAction>()));
+ TEST_CHECK(! id1->supports_action(SupportsActionTest<PretendAction>()));
+ TEST_CHECK(! id1->supports_action(SupportsActionTest<InfoAction>()));
+ TEST_CHECK(id1->supports_action(SupportsActionTest<UninstallAction>()));
+ TEST_CHECK(id1->supports_action(SupportsActionTest<InstalledAction>()));
+ }
+ } test_actions;
+
+ struct UninstallLastTest : TestCase
+ {
+ UninstallLastTest() : TestCase("uninstall last") { }
+
+ void run()
+ {
+ TestEnvironment env;
+
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo2"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root2"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat-one/foo-1.2.3:fred::installed-unpackaged");
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root2/first").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root2/second").is_regular_file());
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/repo2/indices/categories/cat-one/foo").is_symbolic_link());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/repo2/indices/packages/foo/cat-one").is_symbolic_link());
+
+ const tr1::shared_ptr<const PackageID> id(
+ *env.package_database()->query(query::All(), qo_require_exactly_one)->begin());
+
+ UninstallAction action(UninstallActionOptions::create()
+ .no_config_protect(false)
+ );
+ id->perform_action(action);
+
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root2/first").exists());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root2/second").is_regular_file());
+
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/repo2/indices/categories/cat-one/foo").is_symbolic_link());
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/repo2/indices/packages/foo/cat-one").is_symbolic_link());
+
+ repo->invalidate();
+
+ const tr1::shared_ptr<const PackageIDSequence> post_ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(post_ids->begin()), indirect_iterator(post_ids->end()), " "), "");
+ }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+ } test_uninstall_last;
+
+ struct UninstallNotLastTest : TestCase
+ {
+ UninstallNotLastTest() : TestCase("uninstall not last") { }
+
+ void run()
+ {
+ TestEnvironment env;
+
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo3"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root3"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat-one/foo-1.2.3:fred::installed-unpackaged cat-one/foo-3.2.1:barney::installed-unpackaged");
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/repo3/indices/categories/cat-one/foo").is_symbolic_link());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/repo3/indices/packages/foo/cat-one").is_symbolic_link());
+
+ const tr1::shared_ptr<const PackageID> id(
+ *env.package_database()->query(query::Matches(PackageDepSpec("cat-one/foo:fred", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin());
+
+ UninstallAction action(UninstallActionOptions::create()
+ .no_config_protect(false)
+ );
+ id->perform_action(action);
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/repo3/indices/categories/cat-one/foo").is_symbolic_link());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/repo3/indices/packages/foo/cat-one").is_symbolic_link());
+
+ repo->invalidate();
+
+ const tr1::shared_ptr<const PackageIDSequence> post_ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(post_ids->begin()), indirect_iterator(post_ids->end()), " "),
+ "cat-one/foo-3.2.1:barney::installed-unpackaged");
+ }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+ } test_uninstall_not_last;
+
+ struct MultipleOpsTest : TestCase
+ {
+ MultipleOpsTest() : TestCase("multiple ops") { }
+
+ void run()
+ {
+ {
+ TestMessageSuffix suffix("initial", true);
+
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo4"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root4"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "), "");
+
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").exists());
+ }
+
+ {
+ TestMessageSuffix suffix("install 4a", true);
+
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo4"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root4"))
+ ));
+ env.package_database()->add_repository(0, repo);
+
+ tr1::shared_ptr<Repository> source_repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg4a"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/src4a"))
+ ));
+ env.package_database()->add_repository(1, source_repo);
+
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "), "");
+ }
+
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").exists());
+
+ InstallAction action(InstallActionOptions::create()
+ .destination(repo)
+ .no_config_protect(false)
+ .checks(iaco_default)
+ .debug_build(iado_none));
+ (*env.package_database()->query(query::Repository(RepositoryName("unpackaged")),
+ qo_require_exactly_one)->begin())->perform_action(action);
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").is_directory());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4a").is_regular_file());
+
+ repo->invalidate();
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4a-1.0:foo::installed-unpackaged");
+ }
+ }
+
+ {
+ TestMessageSuffix suffix("install 4b1", true);
+
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo4"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root4"))
+ ));
+ env.package_database()->add_repository(0, repo);
+
+ tr1::shared_ptr<Repository> source_repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg4b"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/src4b1"))
+ ));
+ env.package_database()->add_repository(1, source_repo);
+
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4a-1.0:foo::installed-unpackaged");
+ }
+
+ InstallAction action(InstallActionOptions::create()
+ .destination(repo)
+ .no_config_protect(false)
+ .checks(iaco_default)
+ .debug_build(iado_none));
+ (*env.package_database()->query(query::Repository(RepositoryName("unpackaged")),
+ qo_require_exactly_one)->begin())->perform_action(action);
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").is_directory());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4a").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b1").is_regular_file());
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b2").is_regular_file());
+
+ repo->invalidate();
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4a-1.0:foo::installed-unpackaged cat/pkg4b-1.0:foo::installed-unpackaged");
+ }
+ }
+
+ {
+ TestMessageSuffix suffix("install 4b2", true);
+
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo4"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root4"))
+ ));
+ env.package_database()->add_repository(0, repo);
+
+ tr1::shared_ptr<Repository> source_repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg4b"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/src4b2"))
+ ));
+ env.package_database()->add_repository(1, source_repo);
+
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4a-1.0:foo::installed-unpackaged cat/pkg4b-1.0:foo::installed-unpackaged");
+ }
+
+ InstallAction action(InstallActionOptions::create()
+ .destination(repo)
+ .no_config_protect(false)
+ .checks(iaco_default)
+ .debug_build(iado_none));
+ (*env.package_database()->query(query::Repository(RepositoryName("unpackaged")),
+ qo_require_exactly_one)->begin())->perform_action(action);
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").is_directory());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4a").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b").is_regular_file());
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b1").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b2").is_regular_file());
+
+ repo->invalidate();
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4a-1.0:foo::installed-unpackaged cat/pkg4b-1.0:foo::installed-unpackaged");
+ }
+ }
+
+ {
+ TestMessageSuffix suffix("uninstall 4a", true);
+
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo4"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root4"))
+ ));
+ env.package_database()->add_repository(0, repo);
+
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4a-1.0:foo::installed-unpackaged cat/pkg4b-1.0:foo::installed-unpackaged");
+ }
+
+ UninstallAction action(UninstallActionOptions::create()
+ .no_config_protect(false));
+ (*env.package_database()->query(query::Matches(PackageDepSpec("cat/pkg4a", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin())->perform_action(action);
+
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").is_directory());
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4a").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b").is_regular_file());
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b1").is_regular_file());
+ TEST_CHECK(FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir/4b2").is_regular_file());
+
+ repo->invalidate();
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4b-1.0:foo::installed-unpackaged");
+ }
+ }
+
+ {
+ TestMessageSuffix suffix("uninstall 4b", true);
+
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("installed_unpackaged_repository_TEST_dir/repo4"))
+ .root(FSEntry("installed_unpackaged_repository_TEST_dir/root4"))
+ ));
+ env.package_database()->add_repository(0, repo);
+
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "cat/pkg4b-1.0:foo::installed-unpackaged");
+ }
+
+ UninstallAction action(UninstallActionOptions::create()
+ .no_config_protect(false));
+ (*env.package_database()->query(query::Matches(PackageDepSpec("cat/pkg4b", pds_pm_unspecific)),
+ qo_require_exactly_one)->begin())->perform_action(action);
+
+ TEST_CHECK(! FSEntry("installed_unpackaged_repository_TEST_dir/root4/dir").is_directory());
+
+ repo->invalidate();
+ {
+ const tr1::shared_ptr<const PackageIDSequence> pre_ids(
+ env.package_database()->query(query::Repository(RepositoryName("installed-unpackaged")), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(pre_ids->begin()), indirect_iterator(pre_ids->end()), " "),
+ "");
+ }
+ }
+ }
+ } test_multiple_ops;
+}
+
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST_cleanup.sh b/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST_cleanup.sh
new file mode 100755
index 0000000..2063661
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d installed_unpackaged_repository_TEST_dir ] ; then
+ rm -fr installed_unpackaged_repository_TEST_dir
+else
+ true
+fi
+
diff --git a/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST_setup.sh b/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST_setup.sh
new file mode 100755
index 0000000..1018bca
--- /dev/null
+++ b/paludis/repositories/unpackaged/installed_unpackaged_repository_TEST_setup.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir installed_unpackaged_repository_TEST_dir || exit 1
+cd installed_unpackaged_repository_TEST_dir || exit 1
+
+mkdir -p root
+mkdir -p repo1/indices/{categories/cat-one,packages/foo}
+mkdir -p repo1/data/giant-space-weasel/{1:0:foo,2:1:bar}
+ln -s ../../../data/giant-space-weasel repo1/indices/categories/cat-one/foo
+ln -s ../../../data/giant-space-weasel repo1/indices/packages/foo/cat-one
+cat <<"END" > repo1/data/giant-space-weasel/1:0:foo/contents
+type=dir path=/fnord
+END
+cat <<"END" > repo1/data/giant-space-weasel/2:1:bar/contents
+type=dir path=/stilton
+type=file path=/stilton/cheese md5=1234567812345678 mtime=1234
+type=file path=/stilton/is\ delicious md5=8765432187654321 mtime=2345
+END
+
+mkdir -p root2
+cat <<"END" > root2/first
+Eat me!
+END
+cat <<"END" > root2/second
+I got changed.
+END
+
+mkdir -p repo2/indices/{categories/cat-one,packages/foo}
+mkdir -p repo2/data/asdf/1.2.3:fred:ghjk
+ln -s ../../../data/asdf repo2/indices/categories/cat-one/foo
+ln -s ../../../data/asdf repo2/indices/packages/foo/cat-one
+cat <<END > repo2/data/asdf/1.2.3:fred:ghjk/contents
+type=file path=/first md5=c0ba8bfb6501abb1b7105ec79536b848 mtime=$(${PALUDIS_EBUILD_DIR}/utils/getmtime "root2/first")
+type=file path=/first md5=0 mtime=$(${PALUDIS_EBUILD_DIR}/utils/getmtime "root2/second")
+END
+
+mkdir -p root3
+mkdir -p repo3/indices/{categories/cat-one,packages/foo}
+mkdir -p repo3/data/asdf/1.2.3:fred:ghjk
+mkdir -p repo3/data/asdf/3.2.1:barney:qwerty
+ln -s ../../../data/asdf repo3/indices/categories/cat-one/foo
+ln -s ../../../data/asdf repo3/indices/packages/foo/cat-one
+cat <<END > repo3/data/asdf/1.2.3:fred:ghjk/contents
+END
+
+mkdir -p root4
+mkdir -p repo4
+
+mkdir -p src4a/dir
+echo "four a" > src4a/dir/4a
+
+mkdir -p src4b1/dir
+echo "four b 1" > src4b1/dir/4b
+echo "four b 1" > src4b1/dir/4b1
+
+mkdir -p src4b2/dir
+echo "four b 2" > src4b2/dir/4b
+echo "four b 2" > src4b2/dir/4b2
+
diff --git a/paludis/repositories/unpackaged/ndbam-fwd.hh b/paludis/repositories/unpackaged/ndbam-fwd.hh
new file mode 100644
index 0000000..5dd5ba6
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam-fwd.hh
@@ -0,0 +1,31 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_FWD_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_FWD_HH 1
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+ class NDBAM;
+ }
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/ndbam.cc b/paludis/repositories/unpackaged/ndbam.cc
new file mode 100644
index 0000000..c6bb987
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam.cc
@@ -0,0 +1,539 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/ndbam.hh>
+#include <paludis/util/sequence-impl.hh>
+#include <paludis/util/set.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/destringify.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/options.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/util/config_file.hh>
+#include <paludis/hashed_containers.hh>
+#include <paludis/package_id.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/name.hh>
+#include <functional>
+#include <vector>
+#include <map>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+#include <paludis/repositories/unpackaged/ndbam-sr.cc>
+
+template class Sequence<NDBAMEntry>;
+
+namespace
+{
+ struct CategoryContents;
+ struct PackageContents;
+ struct CategoryNamesContainingPackageEntry;
+}
+
+typedef MakeHashedMap<CategoryNamePart, tr1::shared_ptr<CategoryContents> >::Type CategoryContentsMap;
+typedef MakeHashedMap<QualifiedPackageName, tr1::shared_ptr<PackageContents> >::Type PackageContentsMap;
+typedef MakeHashedMap<PackageNamePart, tr1::shared_ptr<CategoryNamesContainingPackageEntry> >::Type CategoryNamesContainingPackage;
+
+namespace
+{
+ struct CategoryContents
+ {
+ Mutex mutex;
+ tr1::shared_ptr<QualifiedPackageNameSet> package_names;
+ PackageContentsMap package_contents_map;
+ };
+
+ struct PackageContents
+ {
+ Mutex mutex;
+ tr1::shared_ptr<NDBAMEntrySequence> entries;
+ };
+
+ struct CategoryNamesContainingPackageEntry
+ {
+ Mutex mutex;
+ tr1::shared_ptr<CategoryNamePartSet> category_names_containing_package;
+ };
+}
+
+
+namespace paludis
+{
+ template <>
+ struct Implementation<NDBAM>
+ {
+ const FSEntry location;
+
+ mutable Mutex category_names_mutex;
+ mutable tr1::shared_ptr<CategoryNamePartSet> category_names;
+ mutable CategoryContentsMap category_contents_map;
+
+ mutable Mutex category_names_containing_package_mutex;
+ mutable CategoryNamesContainingPackage category_names_containing_package;
+
+ Implementation(const FSEntry & l) :
+ location(l)
+ {
+ }
+ };
+}
+
+NDBAM::NDBAM(const FSEntry & l) :
+ PrivateImplementationPattern<NDBAM>(new Implementation<NDBAM>(l))
+{
+ Context c("When creating skeleton NDBAM layout at '" + stringify(l) + "':");
+ (l / "indices").mkdir();
+ (l / "indices" / "categories").mkdir();
+ (l / "indices" / "packages").mkdir();
+ (l / "data").mkdir();
+}
+
+NDBAM::~NDBAM()
+{
+}
+
+tr1::shared_ptr<const CategoryNamePartSet>
+NDBAM::category_names()
+{
+ Lock l(_imp->category_names_mutex);
+ if (! _imp->category_names)
+ {
+ Context context("When loading category names for NDBAM at '" + stringify(_imp->location) + "':");
+ _imp->category_names.reset(new CategoryNamePartSet);
+ for (DirIterator d(_imp->location / "indices" / "categories"), d_end ;
+ d != d_end ; ++d)
+ {
+ if (! d->is_directory_or_symlink_to_directory())
+ continue;
+ if ('-' == d->basename().at(0))
+ continue;
+
+ try
+ {
+ CategoryNamePart c(d->basename());
+ _imp->category_names->insert(c);
+ /* Inserting into category_contents_map might return false if
+ * we're partially populated. That's ok. */
+ _imp->category_contents_map.insert(std::make_pair(c, make_shared_ptr(new CategoryContents)));
+ }
+ catch (const NameError & e)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Skipping directory '" << *d << "' due to exception '"
+ << e.message() << "' (" << e.what() << ")";
+ }
+ }
+ }
+
+ return _imp->category_names;
+}
+
+tr1::shared_ptr<const QualifiedPackageNameSet>
+NDBAM::package_names(const CategoryNamePart & c)
+{
+ if (! has_category_named(c))
+ return make_shared_ptr(new QualifiedPackageNameSet);
+
+ Lock l(_imp->category_names_mutex);
+ CategoryContentsMap::iterator cc_i(_imp->category_contents_map.find(c));
+ if (_imp->category_contents_map.end() == cc_i)
+ throw InternalError(PALUDIS_HERE, "has_category_named(" + stringify(c) + ") but got category_contents_map end");
+ CategoryContents & cc(*cc_i->second);
+
+ l.acquire_then_release_old(cc.mutex);
+
+ if (! cc.package_names)
+ {
+ Context context("When loading package names in '" + stringify(c) + "' for NDBAM at '" + stringify(_imp->location) + "':");
+ cc.package_names.reset(new QualifiedPackageNameSet);
+ for (DirIterator d(_imp->location / "indices" / "categories" / stringify(c)), d_end ;
+ d != d_end ; ++d)
+ {
+ if (! d->is_directory_or_symlink_to_directory())
+ continue;
+ if ('-' == d->basename().at(0))
+ continue;
+
+ try
+ {
+ QualifiedPackageName q(c + PackageNamePart(d->basename()));
+ cc.package_names->insert(q);
+ /* Inserting into package_contents_map might return false if
+ * we're partially populated. That's ok. */
+ cc.package_contents_map.insert(std::make_pair(q, make_shared_ptr(new PackageContents)));
+ }
+ catch (const NameError & e)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Skipping directory '" << *d << "' due to exception '"
+ << e.message() << "' (" << e.what() << ")";
+ }
+ }
+ }
+ return cc.package_names;
+}
+
+bool
+NDBAM::has_category_named(const CategoryNamePart & c)
+{
+ Lock l(_imp->category_names_mutex);
+ if (_imp->category_contents_map.end() != _imp->category_contents_map.find(c))
+ return true;
+
+ if (! _imp->category_names)
+ {
+ if (FSEntry(_imp->location / "indices" / "categories" / stringify(c)).is_directory_or_symlink_to_directory())
+ {
+ _imp->category_contents_map.insert(std::make_pair(c, new CategoryContents));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+NDBAM::has_package_named(const QualifiedPackageName & q)
+{
+ if (! has_category_named(q.category))
+ return false;
+
+ Lock l(_imp->category_names_mutex);
+ CategoryContentsMap::iterator cc_i(_imp->category_contents_map.find(q.category));
+ if (_imp->category_contents_map.end() == cc_i)
+ throw InternalError(PALUDIS_HERE, "has_category_named(" + stringify(q.category) + ") but got category_contents_map end");
+
+ CategoryContents & cc(*cc_i->second);
+ l.acquire_then_release_old(cc.mutex);
+
+ if (cc.package_contents_map.end() != cc.package_contents_map.find(q))
+ return true;
+
+ if (! cc.package_names)
+ {
+ if (FSEntry(_imp->location / "indices" / "categories" / stringify(q.category) / stringify(q.package)).is_directory_or_symlink_to_directory())
+ {
+ cc.package_contents_map.insert(std::make_pair(q, new PackageContents));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+tr1::shared_ptr<NDBAMEntrySequence>
+NDBAM::entries(const QualifiedPackageName & q)
+{
+ if (! has_package_named(q))
+ return make_shared_ptr(new NDBAMEntrySequence);
+
+ Lock l(_imp->category_names_mutex);
+ CategoryContentsMap::iterator cc_i(_imp->category_contents_map.find(q.category));
+ if (_imp->category_contents_map.end() == cc_i)
+ throw InternalError(PALUDIS_HERE, "has_package_named(" + stringify(q) + ") but got category_contents_map end");
+ CategoryContents & cc(*cc_i->second);
+ l.acquire_then_release_old(cc.mutex);
+
+ PackageContentsMap::iterator pc_i(cc.package_contents_map.find(q));
+ if (cc.package_contents_map.end() == pc_i)
+ throw InternalError(PALUDIS_HERE, "has_package_named(" + stringify(q) + ") but got package_contents_map end");
+ PackageContents & pc(*pc_i->second);
+ l.acquire_then_release_old(pc.mutex);
+
+ if (! pc.entries)
+ {
+ pc.entries.reset(new NDBAMEntrySequence);
+ Context context("When loading versions in '" + stringify(q) + "' for NDBAM at '" + stringify(_imp->location) + "':");
+ pc.entries.reset(new NDBAMEntrySequence);
+ for (DirIterator d(_imp->location / "indices" / "categories" / stringify(q.category) / stringify(q.package)), d_end ;
+ d != d_end ; ++d)
+ {
+ if (! d->is_directory_or_symlink_to_directory())
+ continue;
+ if ('-' == d->basename().at(0))
+ continue;
+
+ try
+ {
+ std::vector<std::string> tokens;
+ Tokeniser<delim_kind::AnyOfTag, delim_mode::DelimiterTag> t(":");
+ t.tokenise(d->basename(), std::back_inserter(tokens));
+ if (tokens.size() < 3)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Not using '" << *d <<
+ "', since it contains less than three ':'s";
+ continue;
+ }
+
+ VersionSpec v(tokens[0]);
+ SlotName s(tokens[1]);
+ std::string m(tokens[2]);
+ pc.entries->push_back(make_shared_ptr(new NDBAMEntry(NDBAMEntry::create()
+ .name(q)
+ .version(v)
+ .slot(s)
+ .fs_location(d->realpath())
+ .package_id(tr1::shared_ptr<PackageID>())
+ .magic(m)
+ .mutex(make_shared_ptr(new Mutex)))));
+ }
+ catch (const Exception & e)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Skipping directory '" << *d << "' due to exception '"
+ << e.message() << "' (" << e.what() << ")";
+ }
+ }
+
+ using namespace tr1::placeholders;
+ pc.entries->sort(
+ tr1::bind(std::less<VersionSpec>(),
+ tr1::bind<VersionSpec>(tr1::mem_fn(&NDBAMEntry::version), _1),
+ tr1::bind<VersionSpec>(tr1::mem_fn(&NDBAMEntry::version), _2)
+ ));
+ }
+
+ return pc.entries;
+}
+
+void
+NDBAM::parse_contents(const PackageID & id,
+ const tr1::function<void (const FSEntry &, const std::string & md5, const time_t mtime)> & on_file,
+ const tr1::function<void (const FSEntry &)> & on_dir,
+ const tr1::function<void (const FSEntry &, const std::string & target, const time_t mtime)> & on_sym
+ ) const
+{
+ Context c("When fetching contents for '" + stringify(id) + "':");
+
+ if (! id.fs_location_key())
+ throw InternalError(PALUDIS_HERE, "No id.fs_location_key");
+
+ FSEntry ff(id.fs_location_key()->value() / "contents");
+ if (! ff.is_regular_file_or_symlink_to_regular_file())
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Contents file '" << ff << "' not a regular file, skipping";
+ return;
+ }
+
+ LineConfigFile f(ff, LineConfigFileOptions());
+ for (LineConfigFile::ConstIterator line(f.begin()), line_end(f.end()) ;
+ line != line_end ; ++line)
+ {
+ std::map<std::string, std::string> tokens;
+ std::string::size_type p(0);
+ bool error(false);
+ while ((! error) && (p < line->length()) && (std::string::npos != p))
+ {
+ std::string::size_type q(line->find('=', p));
+ if (std::string::npos == q)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Malformed line '" << *line << "' in '" << ff << "'";
+ error = true;
+ continue;
+ }
+
+ std::string key(line->substr(p, q - p)), value;
+ p = q + 1;
+ while (p < line->length() && std::string::npos != p)
+ {
+ if ('\\' == (*line)[p])
+ {
+ ++p;
+ if (p >= line->length() || std::string::npos == p)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Malformed line '" << *line << "' in '" << ff << "'";
+ error = true;
+ break;
+ }
+ if ('n' == (*line)[p])
+ value.append("\n");
+ else
+ value.append(1, (*line)[p]);
+ ++p;
+ }
+ else if (' ' == (*line)[p])
+ {
+ if (! tokens.insert(std::make_pair(key, value)).second)
+ Log::get_instance()->message(ll_warning, lc_context) << "Duplicate token '" << key << "' on line '"
+ << *line << "' in '" << ff << "'";
+ key.clear();
+ value.clear();
+ ++p;
+ break;
+ }
+ else
+ {
+ value.append(1, (*line)[p]);
+ ++p;
+ }
+ }
+
+ if ((! error) && (! key.empty()))
+ {
+ if (! tokens.insert(std::make_pair(key, value)).second)
+ Log::get_instance()->message(ll_warning, lc_context) << "Duplicate token '" << key << "' on line '"
+ << *line << "' in '" << ff << "'";
+ }
+ }
+
+ if (error)
+ continue;
+
+ if (! tokens.count("type"))
+ {
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "No key 'type' found on line '" << *line << "' in '" << ff << "'";
+ continue;
+ }
+ std::string type(tokens.find("type")->second);
+
+ if (! tokens.count("path"))
+ {
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "No key 'path' found on line '" << *line << "' in '" << ff << "'";
+ continue;
+ }
+ std::string path(tokens.find("path")->second);
+
+ if ("file" == type)
+ {
+ if (! tokens.count("md5"))
+ {
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "No key 'md5' found on sym line '" << *line << "' in '" << ff << "'";
+ continue;
+ }
+ std::string md5(tokens.find("md5")->second);
+
+ if (! tokens.count("mtime"))
+ {
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "No key 'mtime' found on sym line '" << *line << "' in '" << ff << "'";
+ continue;
+ }
+ time_t mtime(destringify<time_t>(tokens.find("mtime")->second));
+
+ on_file(path, md5, mtime);
+ }
+ else if ("dir" == type)
+ {
+ on_dir(path);
+ }
+ else if ("sym" == type)
+ {
+ if (! tokens.count("target"))
+ {
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "No key 'target' found on sym line '" << *line << "' in '" << ff << "'";
+ continue;
+ }
+ std::string target(tokens.find("target")->second);
+
+ if (! tokens.count("mtime"))
+ {
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "No key 'mtime' found on sym line '" << *line << "' in '" << ff << "'";
+ continue;
+ }
+ time_t mtime(destringify<time_t>(tokens.find("mtime")->second));
+
+ on_sym(path, target, mtime);
+ }
+ else
+ Log::get_instance()->message(ll_warning, lc_context) <<
+ "Unknown type '" << type << "' found on line '" << *line << "' in '" << ff << "'";
+ }
+}
+
+tr1::shared_ptr<const CategoryNamePartSet>
+NDBAM::category_names_containing_package(const PackageNamePart & p) const
+{
+ Lock l(_imp->category_names_containing_package_mutex);
+ CategoryNamesContainingPackage::iterator cncp_i(_imp->category_names_containing_package.find(p));
+ if (_imp->category_names_containing_package.end() == cncp_i)
+ cncp_i = _imp->category_names_containing_package.insert(std::make_pair(p, new CategoryNamesContainingPackageEntry)).first;
+ CategoryNamesContainingPackageEntry & cncp(*cncp_i->second);
+
+ l.acquire_then_release_old(cncp.mutex);
+ if (! cncp.category_names_containing_package)
+ {
+ Context c("When finding category names containing package '" + stringify(p) +
+ "' in NDBAM at '" + stringify(_imp->location) + "':");
+
+ cncp.category_names_containing_package.reset(new CategoryNamePartSet);
+ FSEntry dd(_imp->location / "indices" / "packages" / stringify(p));
+ if (dd.is_directory_or_symlink_to_directory())
+ {
+ for (DirIterator d(dd), d_end ;
+ d != d_end ; ++d)
+ {
+ if (! d->is_directory_or_symlink_to_directory())
+ continue;
+ if ('-' == d->basename().at(0))
+ continue;
+
+ try
+ {
+ cncp.category_names_containing_package->insert(CategoryNamePart(d->basename()));
+ }
+ catch (const Exception & e)
+ {
+ Log::get_instance()->message(ll_warning, lc_context) << "Skipping directory '" << *d << "' due to exception '"
+ << e.message() << "' (" << e.what() << ")";
+ }
+ }
+ }
+ }
+
+ return cncp.category_names_containing_package;
+}
+
+void
+NDBAM::deindex(const QualifiedPackageName & q) const
+{
+ Context context("When deindexing '" + stringify(q) + "' in NDBAM at '" + stringify(_imp->location) + "':");
+
+ FSEntry cp_index_sym(_imp->location / "indices" / "categories" / stringify(q.category) / stringify(q.package));
+ cp_index_sym.unlink();
+
+ FSEntry pc_index_sym(_imp->location / "indices" / "packages" / stringify(q.package) / stringify(q.category));
+ pc_index_sym.unlink();
+}
+
+void
+NDBAM::index(const QualifiedPackageName & q, const std::string & d) const
+{
+ Context context("When indexing '" + stringify(q) + "' to '" + stringify(d) +
+ "' in NDBAM at '" + stringify(_imp->location) + "':");
+
+ FSEntry cp_index_sym(_imp->location / "indices" / "categories" / stringify(q.category));
+ cp_index_sym.mkdir();
+ cp_index_sym /= stringify(q.package);
+ if (! cp_index_sym.exists())
+ cp_index_sym.symlink("../../../data/" + d);
+
+ FSEntry pc_index_sym(_imp->location / "indices" / "packages" / stringify(q.package));
+ pc_index_sym.mkdir();
+ pc_index_sym /= stringify(q.category);
+ if (! pc_index_sym.exists())
+ pc_index_sym.symlink("../../../data/" + d);
+}
+
diff --git a/paludis/repositories/unpackaged/ndbam.hh b/paludis/repositories/unpackaged/ndbam.hh
new file mode 100644
index 0000000..b5a0d86
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam.hh
@@ -0,0 +1,82 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_HH 1
+
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/attributes.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/sequence-fwd.hh>
+#include <paludis/util/mutex.hh>
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/name.hh>
+#include <paludis/version_spec.hh>
+#include <paludis/package_id-fwd.hh>
+#include <paludis/contents-fwd.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+
+#include <paludis/repositories/unpackaged/ndbam-sr.hh>
+
+ typedef Sequence<tr1::shared_ptr<NDBAMEntry> > NDBAMEntrySequence;
+
+ class NDBAM :
+ private PrivateImplementationPattern<NDBAM>
+ {
+ public:
+ NDBAM(const FSEntry &);
+ ~NDBAM();
+
+ tr1::shared_ptr<const CategoryNamePartSet> category_names()
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ tr1::shared_ptr<const QualifiedPackageNameSet> package_names(
+ const CategoryNamePart & c)
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ tr1::shared_ptr<const CategoryNamePartSet> category_names_containing_package(
+ const PackageNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ bool has_package_named(const QualifiedPackageName &)
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ bool has_category_named(const CategoryNamePart &)
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ tr1::shared_ptr<NDBAMEntrySequence> entries(const QualifiedPackageName &)
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ void parse_contents(const PackageID &,
+ const tr1::function<void (const FSEntry &, const std::string & md5, const time_t mtime)> & on_file,
+ const tr1::function<void (const FSEntry &)> & on_dir,
+ const tr1::function<void (const FSEntry &, const std::string & target, const time_t mtime)> & on_sym
+ ) const;
+
+ void index(const QualifiedPackageName &, const std::string &) const;
+ void deindex(const QualifiedPackageName &) const;
+ };
+ }
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/ndbam.sr b/paludis/repositories/unpackaged/ndbam.sr
new file mode 100755
index 0000000..2e0ec03
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam.sr
@@ -0,0 +1,18 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et :
+
+make_class_NDBAMEntry()
+{
+ visible
+
+ key name QualifiedPackageName
+ key version VersionSpec
+ key slot SlotName
+ key fs_location FSEntry
+ key magic "std::string"
+ key package_id "tr1::shared_ptr<PackageID>"
+ key mutex "tr1::shared_ptr<Mutex>"
+
+ allow_named_args
+}
+
diff --git a/paludis/repositories/unpackaged/ndbam_merger.cc b/paludis/repositories/unpackaged/ndbam_merger.cc
new file mode 100644
index 0000000..ab2d3c5
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam_merger.cc
@@ -0,0 +1,298 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/ndbam_merger.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/system.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/strip.hh>
+#include <paludis/hook.hh>
+#include <paludis/package_id.hh>
+#include <paludis/md5.hh>
+#include <paludis/environment.hh>
+#include <paludis/package_database.hh>
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <list>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+#include <paludis/repositories/unpackaged/ndbam_merger-sr.cc>
+
+namespace paludis
+{
+ template<>
+ struct Implementation<NDBAMMerger>
+ {
+ NDBAMMergerOptions options;
+ FSEntry realroot;
+ tr1::shared_ptr<std::ofstream> contents_file;
+
+ std::list<std::string> config_protect;
+ std::list<std::string> config_protect_mask;
+
+ Implementation(const NDBAMMergerOptions & o) :
+ options(o),
+ realroot(options.root.realpath())
+ {
+ WhitespaceTokeniser::get_instance()->tokenise(o.config_protect,
+ std::back_inserter(config_protect));
+ WhitespaceTokeniser::get_instance()->tokenise(o.config_protect_mask,
+ std::back_inserter(config_protect_mask));
+ }
+ };
+}
+
+NDBAMMerger::NDBAMMerger(const NDBAMMergerOptions & o) :
+ Merger(MergerOptions::create()
+ .environment(o.environment)
+ .image(o.image)
+ .root(o.root)
+ .no_chown(! getenv_with_default("PALUDIS_NO_CHOWN", "").empty())),
+ PrivateImplementationPattern<NDBAMMerger>(new Implementation<NDBAMMerger>(o))
+{
+}
+
+NDBAMMerger::~NDBAMMerger()
+{
+}
+
+Hook
+NDBAMMerger::extend_hook(const Hook & h)
+{
+ tr1::shared_ptr<const FSEntrySequence> bashrc_files(_imp->options.environment->bashrc_files());
+
+ if (_imp->options.package_id)
+ {
+ std::string cat(stringify(_imp->options.package_id->name().category));
+ std::string pn(stringify(_imp->options.package_id->name().package));
+ std::string pvr(stringify(_imp->options.package_id->version()));
+ std::string pv(stringify(_imp->options.package_id->version().remove_revision()));
+ std::string slot(stringify(_imp->options.package_id->slot()));
+
+ return Merger::extend_hook(h)
+ ("P", pn + "-" + pv)
+ ("PN", pn)
+ ("CATEGORY", cat)
+ ("PR", _imp->options.package_id->version().revision_only())
+ ("PV", pv)
+ ("PVR", pvr)
+ ("PF", pn + "-" + pvr)
+ ("SLOT", slot)
+ ("CONFIG_PROTECT", _imp->options.config_protect)
+ ("CONFIG_PROTECT_MASK", _imp->options.config_protect_mask)
+ ("PALUDIS_BASHRC_FILES", join(bashrc_files->begin(), bashrc_files->end(), " "));
+ }
+ else
+ return Merger::extend_hook(h)
+ ("CONFIG_PROTECT", _imp->options.config_protect)
+ ("CONFIG_PROTECT_MASK", _imp->options.config_protect_mask)
+ ("PALUDIS_BASHRC_FILES", join(bashrc_files->begin(), bashrc_files->end(), " "));
+}
+
+namespace
+{
+ std::string escape(const std::string & s)
+ {
+ std::string result;
+ for (std::string::size_type p(0), p_end(s.length()) ;
+ p != p_end ; ++p)
+ {
+ if (s[p] >= 'a' && s[p] <= 'z')
+ result.append(1, s[p]);
+ else if (s[p] >= 'A' && s[p] <= 'Z')
+ result.append(1, s[p]);
+ else if (s[p] >= '0' && s[p] <= '9')
+ result.append(1, s[p]);
+ else if (s[p] == '/' || s[p] == '-' || s[p] == '_' || s[p] == '.')
+ result.append(1, s[p]);
+ else if (s[p] == '\n')
+ result.append("\\n");
+ else
+ result.append("\\" + stringify(s[p]));
+ }
+ return result;
+ }
+}
+
+void
+NDBAMMerger::record_install_file(const FSEntry & src, const FSEntry & dst_dir, const std::string & dst_name)
+{
+ std::string tidy(stringify((dst_dir / dst_name).strip_leading(_imp->realroot))),
+ tidy_real(stringify((dst_dir / src.basename()).strip_leading(_imp->realroot)));
+ time_t timestamp((dst_dir / dst_name).mtime());
+
+ std::ifstream infile(stringify(FSEntry(dst_dir / dst_name)).c_str());
+ if (! infile)
+ throw MergerError("Cannot read '" + stringify(FSEntry(dst_dir / dst_name)) + "'");
+
+ MD5 md5(infile);
+
+ std::string line(">>> [obj] " + tidy_real);
+ if (tidy_real != tidy)
+ line.append(" (" + FSEntry(tidy).basename() + ")");
+ display_override(line);
+
+ *_imp->contents_file << "type=file";
+ *_imp->contents_file << " path=" << escape(tidy_real);
+ *_imp->contents_file << " md5=" << md5.hexsum();
+ *_imp->contents_file << " mtime=" << timestamp;
+ *_imp->contents_file << std::endl;
+}
+
+void
+NDBAMMerger::record_install_dir(const FSEntry & src, const FSEntry & dst_dir)
+{
+ std::string tidy(stringify((dst_dir / src.basename()).strip_leading(_imp->realroot)));
+ display_override(">>> [dir] " + tidy);
+
+ *_imp->contents_file << "type=dir path=" << escape(tidy) << std::endl;
+}
+
+void
+NDBAMMerger::record_install_sym(const FSEntry & src, const FSEntry & dst_dir)
+{
+ std::string tidy(stringify((dst_dir / src.basename()).strip_leading(_imp->realroot)));
+ std::string target((dst_dir / src.basename()).readlink());
+ time_t timestamp((dst_dir / src.basename()).mtime());
+
+ display_override(">>> [sym] " + tidy);
+
+ *_imp->contents_file << "type=sym path=" << escape(tidy);
+ *_imp->contents_file << " target=" << escape(target);
+ *_imp->contents_file << " mtime=" << timestamp << std::endl;
+}
+
+void
+NDBAMMerger::on_error(bool is_check, const std::string & s)
+{
+ make_check_fail();
+
+ if (is_check)
+ std::cout << "." << std::endl << "!!! " << s << std::endl;
+ else
+ throw MergerError(s);
+}
+
+void
+NDBAMMerger::on_warn(bool is_check, const std::string & s)
+{
+ if (is_check)
+ Log::get_instance()->message(ll_warning, lc_context, s);
+}
+
+bool
+NDBAMMerger::config_protected(const FSEntry & src, const FSEntry & dst_dir)
+{
+ std::string tidy(stringify((dst_dir / src.basename()).strip_leading(_imp->realroot)));
+
+ bool result(false);
+ for (std::list<std::string>::const_iterator c(_imp->config_protect.begin()),
+ c_end(_imp->config_protect.end()) ; c != c_end && ! result ; ++c)
+ {
+ std::string cc(strip_trailing(*c, "/") + "/");
+ if (tidy == *c || 0 == tidy.compare(0, cc.length(), cc))
+ result = true;
+ }
+ if (result)
+ for (std::list<std::string>::const_iterator c(_imp->config_protect_mask.begin()),
+ c_end(_imp->config_protect_mask.end()) ; c != c_end && result ; ++c)
+ {
+ std::string cc(strip_trailing(*c, "/") + "/");
+ if (tidy == *c || 0 == tidy.compare(0, cc.length(), cc))
+ result = false;
+ }
+
+ return result;
+}
+
+std::string
+NDBAMMerger::make_config_protect_name(const FSEntry & src, const FSEntry & dst)
+{
+ std::string result_name(src.basename());
+ int n(0);
+
+ std::ifstream our_md5_file(stringify(src).c_str());
+ if (! our_md5_file)
+ throw MergerError("Could not get md5 for '" + stringify((dst / src.basename()).strip_leading(_imp->realroot)) + "'");
+ MD5 our_md5(our_md5_file);
+
+ while (true)
+ {
+ if (! (dst / result_name).exists())
+ break;
+
+ if ((dst / result_name).is_regular_file_or_symlink_to_regular_file())
+ {
+ std::ifstream other_md5_file(stringify(dst / result_name).c_str());
+ if (other_md5_file)
+ {
+ MD5 other_md5(other_md5_file);
+ if (our_md5.hexsum() == other_md5.hexsum())
+ break;
+ }
+ }
+
+ std::stringstream s;
+ s << std::setw(4) << std::setfill('0') << std::right << n++;
+ result_name = "._cfg" + s.str() + "_" + src.basename();
+ }
+
+ return result_name;
+}
+
+void
+NDBAMMerger::merge()
+{
+ display_override(">>> Merging to " + stringify(_imp->options.root));
+ _imp->contents_file.reset(new std::ofstream(stringify(_imp->options.contents_file).c_str()));
+ Merger::merge();
+}
+
+bool
+NDBAMMerger::check()
+{
+ std::cout << ">>> Checking whether we can merge to " << _imp->options.root << " ";
+ bool result(Merger::check());
+ std::cout << std::endl;
+ return result;
+}
+
+void
+NDBAMMerger::on_enter_dir(bool is_check, const FSEntry)
+{
+ if (! is_check)
+ return;
+
+ std::cout << "." << std::flush;
+}
+
+void
+NDBAMMerger::display_override(const std::string & message) const
+{
+ std::cout << message << std::endl;
+}
+
diff --git a/paludis/repositories/unpackaged/ndbam_merger.hh b/paludis/repositories/unpackaged/ndbam_merger.hh
new file mode 100644
index 0000000..8d85151
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam_merger.hh
@@ -0,0 +1,62 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_MERGER_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_MERGER_HH 1
+
+#include <paludis/merger.hh>
+#include <paludis/package_id-fwd.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+#include <paludis/repositories/unpackaged/ndbam_merger-sr.hh>
+
+ class NDBAMMerger :
+ public Merger,
+ private PrivateImplementationPattern<NDBAMMerger>
+ {
+ private:
+ void display_override(const std::string &) const;
+
+ public:
+ NDBAMMerger(const NDBAMMergerOptions &);
+ ~NDBAMMerger();
+
+ virtual Hook extend_hook(const Hook &);
+
+ virtual void record_install_file(const FSEntry &, const FSEntry &, const std::string &);
+ virtual void record_install_dir(const FSEntry &, const FSEntry &);
+ virtual void record_install_sym(const FSEntry &, const FSEntry &);
+
+ virtual void on_error(bool is_check, const std::string &);
+ virtual void on_warn(bool is_check, const std::string &);
+ virtual void on_enter_dir(bool is_check, const FSEntry);
+
+ virtual bool config_protected(const FSEntry &, const FSEntry &);
+ virtual std::string make_config_protect_name(const FSEntry &, const FSEntry &);
+
+ virtual void merge();
+ virtual bool check();
+ };
+ }
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/ndbam_merger.sr b/paludis/repositories/unpackaged/ndbam_merger.sr
new file mode 100644
index 0000000..529fa21
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam_merger.sr
@@ -0,0 +1,18 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et :
+
+make_class_NDBAMMergerOptions()
+{
+ visible
+
+ key environment "Environment *"
+ key image "FSEntry"
+ key root "FSEntry"
+ key contents_file "FSEntry"
+ key config_protect std::string
+ key config_protect_mask std::string
+ key package_id "tr1::shared_ptr<const PackageID>"
+
+ allow_named_args
+}
+
diff --git a/paludis/repositories/unpackaged/ndbam_unmerger.cc b/paludis/repositories/unpackaged/ndbam_unmerger.cc
new file mode 100644
index 0000000..1ca1c9e
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam_unmerger.cc
@@ -0,0 +1,295 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/repositories/unpackaged/ndbam_unmerger.hh>
+#include <paludis/repositories/unpackaged/ndbam.hh>
+
+#include <paludis/util/destringify.hh>
+#include <paludis/md5.hh>
+#include <paludis/environment.hh>
+#include <paludis/hook.hh>
+#include <paludis/package_database.hh>
+#include <paludis/package_id.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/strip.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+#include <libwrapiter/libwrapiter_output_iterator.hh>
+
+#include <list>
+#include <map>
+#include <vector>
+#include <fstream>
+#include <iostream>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+#include <paludis/repositories/unpackaged/ndbam_unmerger-sr.cc>
+
+namespace paludis
+{
+ template<>
+ struct Implementation<NDBAMUnmerger>
+ {
+ NDBAMUnmergerOptions options;
+
+ std::list<std::string> config_protect;
+ std::list<std::string> config_protect_mask;
+
+ Implementation(const NDBAMUnmergerOptions & o) :
+ options(o)
+ {
+ WhitespaceTokeniser::get_instance()->tokenise(o.config_protect,
+ std::back_inserter(config_protect));
+ WhitespaceTokeniser::get_instance()->tokenise(o.config_protect_mask,
+ std::back_inserter(config_protect_mask));
+ }
+ };
+}
+
+class NDBAMUnmerger::FileExtraInfo :
+ public Unmerger::ExtraInfo
+{
+ public:
+ std::string _md5sum;
+ time_t _mtime;
+
+ FileExtraInfo(std::string md5sum, time_t mtime) :
+ _md5sum(md5sum),
+ _mtime(mtime)
+ {
+ }
+
+ virtual ~FileExtraInfo()
+ {
+ }
+};
+
+class NDBAMUnmerger::SymlinkExtraInfo :
+ public Unmerger::ExtraInfo
+{
+ public:
+ std::string _dest;
+ time_t _mtime;
+
+ SymlinkExtraInfo(std::string dest, time_t mtime) :
+ _dest(dest),
+ _mtime(mtime)
+ {
+ }
+
+ virtual ~SymlinkExtraInfo()
+ {
+ }
+};
+
+NDBAMUnmerger::NDBAMUnmerger(const NDBAMUnmergerOptions & o) :
+ Unmerger(UnmergerOptions::create()
+ .environment(o.environment)
+ .root(o.root)),
+ PrivateImplementationPattern<NDBAMUnmerger>(new Implementation<NDBAMUnmerger>(o)),
+ _imp(PrivateImplementationPattern<NDBAMUnmerger>::_imp.get())
+{
+}
+
+NDBAMUnmerger::~NDBAMUnmerger()
+{
+}
+
+Hook
+NDBAMUnmerger::extend_hook(const Hook & h) const
+{
+ tr1::shared_ptr<const FSEntrySequence> bashrc_files(_imp->options.environment->bashrc_files());
+
+ Hook result(Unmerger::extend_hook(h)
+ ("CONFIG_PROTECT", _imp->options.config_protect)
+ ("CONFIG_PROTECT_MASK", _imp->options.config_protect_mask)
+ ("PALUDIS_BASHRC_FILES", join(bashrc_files->begin(), bashrc_files->end(), " ")));
+
+ if (_imp->options.package_id)
+ {
+ std::string cat(stringify(_imp->options.package_id->name().category));
+ std::string pn(stringify(_imp->options.package_id->name().package));
+ std::string pvr(stringify(_imp->options.package_id->version()));
+ std::string pv(stringify(_imp->options.package_id->version().remove_revision()));
+
+ return result
+ ("P", pn + "-" + pv)
+ ("PN", pn)
+ ("CATEGORY", cat)
+ ("PR", _imp->options.package_id->version().revision_only())
+ ("PV", pv)
+ ("PVR", pvr)
+ ("PF", pn + "-" + pvr)
+ ("SLOT", stringify(_imp->options.package_id->slot()));
+ }
+
+ return result;
+}
+
+bool
+NDBAMUnmerger::config_protected(const FSEntry & f) const
+{
+ std::string tidy(make_tidy(f));
+
+ bool result(false);
+ for (std::list<std::string>::const_iterator c(_imp->config_protect.begin()),
+ c_end(_imp->config_protect.end()) ; c != c_end && ! result ; ++c)
+ {
+ std::string cc(strip_trailing(*c, "/") + "/");
+ if (tidy == *c || 0 == tidy.compare(0, cc.length(), cc))
+ result = true;
+ }
+ if (result)
+ for (std::list<std::string>::const_iterator c(_imp->config_protect_mask.begin()),
+ c_end(_imp->config_protect_mask.end()) ; c != c_end && result ; ++c)
+ {
+ std::string cc(strip_trailing(*c, "/") + "/");
+ if (tidy == *c || 0 == tidy.compare(0, cc.length(), cc))
+ result = false;
+ }
+
+ return result;
+}
+
+std::string
+NDBAMUnmerger::make_tidy(const FSEntry & f) const
+{
+ std::string root_str(stringify(_imp->options.root)), f_str(stringify(f));
+ if (root_str == "/")
+ root_str.clear();
+ if (0 != f_str.compare(0, root_str.length(), root_str))
+ throw NDBAMUnmergerError("Can't work out tidy name for '" + f_str + "' with root '" + root_str + "'");
+ return f_str.substr(root_str.length());
+}
+
+void
+NDBAMUnmerger::_add_file(const FSEntry & f, const std::string & md5, const time_t mtime)
+{
+ add_unmerge_entry(stringify(f), et_file, make_shared_ptr(new FileExtraInfo(md5, mtime)));
+}
+
+void
+NDBAMUnmerger::_add_dir(const FSEntry & f)
+{
+ add_unmerge_entry(stringify(f), et_dir, tr1::shared_ptr<ExtraInfo>());
+}
+
+void
+NDBAMUnmerger::_add_sym(const FSEntry & f, const std::string & target, const time_t mtime)
+{
+ add_unmerge_entry(stringify(f), et_sym, make_shared_ptr(new SymlinkExtraInfo(target, mtime)));
+}
+
+void
+NDBAMUnmerger::populate_unmerge_set()
+{
+ using namespace tr1::placeholders;
+ _imp->options.ndbam->parse_contents(*_imp->options.package_id,
+ tr1::bind(&NDBAMUnmerger::_add_file, this, _1, _2, _3),
+ tr1::bind(&NDBAMUnmerger::_add_dir, this, _1),
+ tr1::bind(&NDBAMUnmerger::_add_sym, this, _1, _2, _3)
+ );
+}
+
+bool
+NDBAMUnmerger::check_file(const FSEntry & f, tr1::shared_ptr<ExtraInfo> ei) const
+{
+ tr1::shared_ptr<FileExtraInfo> fie(tr1::static_pointer_cast<FileExtraInfo>(ei));
+
+ if (! (_imp->options.root / f).is_regular_file())
+ display("--- [!type] " + stringify(f));
+ else if ((_imp->options.root / f).mtime() != fie->_mtime)
+ display("--- [!time] " + stringify(f));
+ else
+ {
+ std::ifstream md5_file(stringify(_imp->options.root / f).c_str());
+ if (! md5_file)
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context, "Cannot get md5 for '" +
+ stringify(_imp->options.root / f) + "'");
+ display("--- [!md5?] " + stringify(f));
+ }
+ else if (MD5(md5_file).hexsum() != fie->_md5sum)
+ display("--- [!md5 ] " + stringify(f));
+ else if (config_protected(_imp->options.root / f))
+ display("--- [cfgpr] " + stringify(f));
+ else
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NDBAMUnmerger::check_sym(const FSEntry & f, tr1::shared_ptr<ExtraInfo> ei) const
+{
+ tr1::shared_ptr<SymlinkExtraInfo> sie(tr1::static_pointer_cast<SymlinkExtraInfo>(ei));
+
+ if (! (_imp->options.root / f).is_symbolic_link())
+ display("--- [!type] " + stringify(f));
+ else if ((_imp->options.root / f).mtime() != sie->_mtime)
+ display("--- [!time] " + stringify(f));
+ else if ((_imp->options.root / f).readlink() != sie->_dest)
+ display("--- [!dest] " + stringify(f));
+ else
+ return true;
+
+ return false;
+}
+
+bool
+NDBAMUnmerger::check_misc(const FSEntry &, tr1::shared_ptr<ExtraInfo>) const
+{
+ return false;
+}
+
+bool
+NDBAMUnmerger::check_dir(const FSEntry & f, tr1::shared_ptr<ExtraInfo>) const
+{
+ if (! (_imp->options.root / f).is_directory())
+ display("--- [!type] " + stringify(f));
+ else if (DirIterator(_imp->options.root / f, false) != DirIterator())
+ display("--- [!empt] " + stringify(f));
+ else
+ return true;
+
+ return false;
+}
+
+void
+NDBAMUnmerger::display(const std::string & message) const
+{
+ std::cout << message << std::endl;
+}
+
+NDBAMUnmergerError::NDBAMUnmergerError(const std::string & s) throw () :
+ UnmergerError(s)
+{
+}
+
diff --git a/paludis/repositories/unpackaged/ndbam_unmerger.hh b/paludis/repositories/unpackaged/ndbam_unmerger.hh
new file mode 100644
index 0000000..5e64602
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam_unmerger.hh
@@ -0,0 +1,84 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_UNMERGER_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_NDBAM_UNMERGER_HH 1
+
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/sr.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/unmerger.hh>
+#include <paludis/environment-fwd.hh>
+#include <paludis/package_id-fwd.hh>
+
+namespace paludis
+{
+
+ namespace unpackaged_repositories
+ {
+ class NDBAM;
+
+#include <paludis/repositories/unpackaged/ndbam_unmerger-sr.hh>
+
+ class PALUDIS_VISIBLE NDBAMUnmergerError :
+ public UnmergerError
+ {
+ public:
+ NDBAMUnmergerError(const std::string &) throw ();
+ };
+
+ class PALUDIS_VISIBLE NDBAMUnmerger :
+ public Unmerger,
+ private PrivateImplementationPattern<NDBAMUnmerger>
+ {
+ private:
+ Implementation<NDBAMUnmerger> * _imp;
+ class FileExtraInfo;
+ class SymlinkExtraInfo;
+
+ void _add_file(const FSEntry & f, const std::string & md5, const time_t mtime);
+ void _add_dir(const FSEntry & f);
+ void _add_sym(const FSEntry & f, const std::string & target, const time_t mtime);
+
+ protected:
+ bool config_protected(const FSEntry &) const;
+ std::string make_tidy(const FSEntry &) const;
+
+ void populate_unmerge_set();
+
+ void display(const std::string &) const;
+
+ bool check_file(const FSEntry &, tr1::shared_ptr<ExtraInfo>) const;
+ bool check_dir(const FSEntry &, tr1::shared_ptr<ExtraInfo>) const;
+ bool check_sym(const FSEntry &, tr1::shared_ptr<ExtraInfo>) const;
+ bool check_misc(const FSEntry &, tr1::shared_ptr<ExtraInfo>) const;
+
+ public:
+ NDBAMUnmerger(const NDBAMUnmergerOptions &);
+ ~NDBAMUnmerger();
+
+ virtual Hook extend_hook(const Hook &) const;
+ };
+ }
+
+}
+
+
+#endif
diff --git a/paludis/repositories/unpackaged/ndbam_unmerger.sr b/paludis/repositories/unpackaged/ndbam_unmerger.sr
new file mode 100644
index 0000000..31b4f4b
--- /dev/null
+++ b/paludis/repositories/unpackaged/ndbam_unmerger.sr
@@ -0,0 +1,19 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et :
+
+make_class_NDBAMUnmergerOptions()
+{
+ visible
+
+ key environment "const Environment *"
+ key root "FSEntry"
+ key contents_file "FSEntry"
+ key config_protect std::string
+ key config_protect_mask std::string
+ key package_id "tr1::shared_ptr<const PackageID>"
+ key ndbam "const NDBAM *"
+
+ allow_named_args
+}
+
+
diff --git a/paludis/repositories/unpackaged/registration.cc b/paludis/repositories/unpackaged/registration.cc
new file mode 100644
index 0000000..f8e9233
--- /dev/null
+++ b/paludis/repositories/unpackaged/registration.cc
@@ -0,0 +1,97 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repository_maker.hh>
+#include <paludis/util/attributes.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/repositories/unpackaged/installed_unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/exceptions.hh>
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+
+using namespace paludis;
+
+namespace
+{
+ tr1::shared_ptr<Repository>
+ make_unpackaged_repository(
+ Environment * const env,
+ tr1::shared_ptr<const Map<std::string, std::string> > m)
+ {
+ Context context("When creating UnpackagedRepository:");
+
+ std::string location;
+ if (m->end() == m->find("location") || ((location = m->find("location")->second)).empty())
+ throw unpackaged_repositories::RepositoryConfigurationError("Key 'location' not specified or empty");
+
+ std::string name;
+ if (m->end() == m->find("name") || ((name = m->find("name")->second)).empty())
+ throw unpackaged_repositories::RepositoryConfigurationError("Key 'name' not specified or empty");
+
+ std::string version;
+ if (m->end() == m->find("version") || ((version = m->find("version")->second)).empty())
+ throw unpackaged_repositories::RepositoryConfigurationError("Key 'version' not specified or empty");
+
+ std::string slot;
+ if (m->end() == m->find("slot") || ((slot = m->find("slot")->second)).empty())
+ throw unpackaged_repositories::RepositoryConfigurationError("Key 'slot' not specified or empty");
+
+ return make_shared_ptr(new UnpackagedRepository(RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(env)
+ .location(location)
+ .name(QualifiedPackageName(name))
+ .version(VersionSpec(version))
+ .slot(SlotName(slot))));
+ }
+
+ tr1::shared_ptr<Repository>
+ make_installed_unpackaged_repository(
+ Environment * const env,
+ tr1::shared_ptr<const Map<std::string, std::string> > m)
+ {
+ Context context("When creating InstalledUnpackagedRepository:");
+
+ std::string location;
+ if (m->end() == m->find("location") || ((location = m->find("location")->second)).empty())
+ throw unpackaged_repositories::RepositoryConfigurationError("Key 'location' not specified or empty");
+
+ std::string root;
+ if (m->end() == m->find("root") || ((root = m->find("root")->second)).empty())
+ throw unpackaged_repositories::RepositoryConfigurationError("Key 'root' not specified or empty");
+
+ return make_shared_ptr(new InstalledUnpackagedRepository(RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(env)
+ .location(location)
+ .root(root)));
+ }
+}
+
+extern "C"
+{
+ void PALUDIS_VISIBLE register_repositories(RepositoryMaker * maker);
+}
+
+void register_repositories(RepositoryMaker * maker)
+{
+ maker->register_maker("unpackaged", &make_unpackaged_repository);
+ maker->register_maker("installed_unpackaged", &make_installed_unpackaged_repository);
+}
+
diff --git a/paludis/repositories/unpackaged/unpackaged_id.cc b/paludis/repositories/unpackaged/unpackaged_id.cc
new file mode 100644
index 0000000..2386cbc
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_id.cc
@@ -0,0 +1,320 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/unpackaged_id.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/visitor-impl.hh>
+#include <paludis/util/visitor_cast.hh>
+#include <paludis/name.hh>
+#include <paludis/version_spec.hh>
+#include <paludis/package_database.hh>
+#include <paludis/environment.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/action.hh>
+#include <paludis/hashed_containers.hh>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+namespace
+{
+ class UnpackagedFSEntryKey :
+ public MetadataFSEntryKey
+ {
+ private:
+ const FSEntry _location;
+
+ public:
+ UnpackagedFSEntryKey(const FSEntry & l) :
+ MetadataFSEntryKey("location", "Location", mkt_normal),
+ _location(l)
+ {
+ }
+
+ const FSEntry value() const
+ {
+ return _location;
+ }
+ };
+}
+
+namespace paludis
+{
+ template <>
+ struct Implementation<UnpackagedID>
+ {
+ const Environment * const env;
+ const QualifiedPackageName name;
+ const VersionSpec version;
+ const SlotName slot;
+ const RepositoryName repository_name;
+
+ tr1::shared_ptr<UnpackagedFSEntryKey> fs_location_key;
+
+ Implementation(const Environment * const e,
+ const QualifiedPackageName & q,
+ const VersionSpec & v,
+ const SlotName & s,
+ const RepositoryName & r,
+ const FSEntry & l) :
+ env(e),
+ name(q),
+ version(v),
+ slot(s),
+ repository_name(r),
+ fs_location_key(new UnpackagedFSEntryKey(l))
+ {
+ }
+ };
+}
+
+UnpackagedID::UnpackagedID(const Environment * const e, const QualifiedPackageName & q,
+ const VersionSpec & v, const SlotName & s, const RepositoryName & n, const FSEntry & l) :
+ PrivateImplementationPattern<UnpackagedID>(new Implementation<UnpackagedID>(e, q, v, s, n, l)),
+ _imp(PrivateImplementationPattern<UnpackagedID>::_imp.get())
+{
+ add_metadata_key(_imp->fs_location_key);
+}
+
+UnpackagedID::~UnpackagedID()
+{
+}
+
+void
+UnpackagedID::need_keys_added() const
+{
+}
+
+void
+UnpackagedID::need_masks_added() const
+{
+}
+
+const std::string
+UnpackagedID::canonical_form(const PackageIDCanonicalForm f) const
+{
+ switch (f)
+ {
+ case idcf_full:
+ return stringify(_imp->name) + "-" + stringify(_imp->version) + ":" +
+ stringify(slot()) + "::" + stringify(_imp->repository_name);
+
+ case idcf_version:
+ return stringify(_imp->version);
+
+ case idcf_no_version:
+ return stringify(_imp->name) + ":" + stringify(slot()) + "::" +
+ stringify(_imp->repository_name);
+
+ case last_idcf:
+ break;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Bad PackageIDCanonicalForm");
+}
+
+const QualifiedPackageName
+UnpackagedID::name() const
+{
+ return _imp->name;
+}
+
+const VersionSpec
+UnpackagedID::version() const
+{
+ return _imp->version;
+}
+
+const SlotName
+UnpackagedID::slot() const
+{
+ return _imp->slot;
+}
+
+const tr1::shared_ptr<const Repository>
+UnpackagedID::repository() const
+{
+ return _imp->env->package_database()->fetch_repository(_imp->repository_name);
+}
+
+const tr1::shared_ptr<const MetadataPackageIDKey>
+UnpackagedID::virtual_for_key() const
+{
+ return tr1::shared_ptr<const MetadataPackageIDKey>();
+}
+
+const tr1::shared_ptr<const MetadataSetKey<KeywordNameSet> >
+UnpackagedID::keywords_key() const
+{
+ return tr1::shared_ptr<const MetadataSetKey<KeywordNameSet> >();
+}
+
+const tr1::shared_ptr<const MetadataSetKey<IUseFlagSet> >
+UnpackagedID::iuse_key() const
+{
+ return tr1::shared_ptr<const MetadataSetKey<IUseFlagSet> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<ProvideSpecTree> >
+UnpackagedID::provide_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<ProvideSpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSetKey<PackageIDSequence> >
+UnpackagedID::contains_key() const
+{
+ return tr1::shared_ptr<const MetadataSetKey<PackageIDSequence> >();
+}
+
+const tr1::shared_ptr<const MetadataPackageIDKey>
+UnpackagedID::contained_in_key() const
+{
+ return tr1::shared_ptr<const MetadataPackageIDKey>();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+UnpackagedID::build_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+UnpackagedID::run_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+UnpackagedID::post_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >
+UnpackagedID::suggested_dependencies_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> >
+UnpackagedID::fetches_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> >
+UnpackagedID::homepage_key() const
+{
+ return tr1::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> >();
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+UnpackagedID::short_description_key() const
+{
+ return tr1::shared_ptr<const MetadataStringKey>();
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+UnpackagedID::long_description_key() const
+{
+ return tr1::shared_ptr<const MetadataStringKey>();
+}
+
+const tr1::shared_ptr<const MetadataContentsKey>
+UnpackagedID::contents_key() const
+{
+ return tr1::shared_ptr<const MetadataContentsKey>();
+}
+
+const tr1::shared_ptr<const MetadataTimeKey>
+UnpackagedID::installed_time_key() const
+{
+ return tr1::shared_ptr<const MetadataTimeKey>();
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+UnpackagedID::source_origin_key() const
+{
+ return tr1::shared_ptr<const MetadataStringKey>();
+}
+
+const tr1::shared_ptr<const MetadataStringKey>
+UnpackagedID::binary_origin_key() const
+{
+ return tr1::shared_ptr<const MetadataStringKey>();
+}
+
+const tr1::shared_ptr<const MetadataFSEntryKey>
+UnpackagedID::fs_location_key() const
+{
+ return _imp->fs_location_key;
+}
+
+bool
+UnpackagedID::supports_action(const SupportsActionTestBase & test) const
+{
+ return visitor_cast<const SupportsActionTest<InstallAction> >(test);
+}
+
+void
+UnpackagedID::perform_action(Action & action) const
+{
+ const InstallAction * const install_action(visitor_cast<const InstallAction>(action));
+ if (! install_action)
+ throw UnsupportedActionError(*this, action);
+
+ if (! install_action->options.destination->destination_interface)
+ throw InstallActionError("Can't install '" + stringify(*this)
+ + "' to destination '" + stringify(install_action->options.destination->name())
+ + "' because destination does not provide destination_interface");
+
+ install_action->options.destination->destination_interface->merge(
+ MergeOptions::create()
+ .package_id(shared_from_this())
+ .image_dir(fs_location_key()->value())
+ .environment_file(FSEntry("/dev/null"))
+ );
+}
+
+void
+UnpackagedID::invalidate_masks() const
+{
+}
+
+bool
+UnpackagedID::breaks_portage() const
+{
+ return true;
+}
+
+bool
+UnpackagedID::arbitrary_less_than_comparison(const PackageID & other) const
+{
+ return slot().data() < other.slot().data();
+}
+
+std::size_t
+UnpackagedID::extra_hash_value() const
+{
+ return CRCHash<SlotName>()(slot());
+}
+
diff --git a/paludis/repositories/unpackaged/unpackaged_id.hh b/paludis/repositories/unpackaged/unpackaged_id.hh
new file mode 100644
index 0000000..e00a667
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_id.hh
@@ -0,0 +1,90 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_UNPACKAGED_ID_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_UNPACKAGED_ID_HH 1
+
+#include <paludis/package_id.hh>
+#include <paludis/name-fwd.hh>
+#include <paludis/util/fs_entry-fwd.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/tr1_memory.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+ class UnpackagedID :
+ public PackageID,
+ private PrivateImplementationPattern<UnpackagedID>,
+ public tr1::enable_shared_from_this<UnpackagedID>
+ {
+ private:
+ Implementation<UnpackagedID> * const _imp;
+
+ protected:
+ void need_keys_added() const;
+ void need_masks_added() const;
+
+ public:
+ UnpackagedID(const Environment * const, const QualifiedPackageName &, const VersionSpec &,
+ const SlotName &, const RepositoryName &, const FSEntry &);
+
+ ~UnpackagedID();
+
+ virtual const std::string canonical_form(const PackageIDCanonicalForm) const;
+ virtual const QualifiedPackageName name() const;
+ virtual const VersionSpec version() const;
+ virtual const SlotName slot() const;
+ virtual const tr1::shared_ptr<const Repository> repository() const;
+
+ virtual const tr1::shared_ptr<const MetadataPackageIDKey> virtual_for_key() const;
+ virtual const tr1::shared_ptr<const MetadataSetKey<KeywordNameSet> > keywords_key() const;
+ virtual const tr1::shared_ptr<const MetadataSetKey<IUseFlagSet> > iuse_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<ProvideSpecTree> > provide_key() const;
+ virtual const tr1::shared_ptr<const MetadataSetKey<PackageIDSequence> > contains_key() const;
+ virtual const tr1::shared_ptr<const MetadataPackageIDKey> contained_in_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > build_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > run_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > post_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<DependencySpecTree> > suggested_dependencies_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<FetchableURISpecTree> > fetches_key() const;
+ virtual const tr1::shared_ptr<const MetadataSpecTreeKey<SimpleURISpecTree> > homepage_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> short_description_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> long_description_key() const;
+ virtual const tr1::shared_ptr<const MetadataContentsKey> contents_key() const;
+ virtual const tr1::shared_ptr<const MetadataTimeKey> installed_time_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> source_origin_key() const;
+ virtual const tr1::shared_ptr<const MetadataStringKey> binary_origin_key() const;
+ virtual const tr1::shared_ptr<const MetadataFSEntryKey> fs_location_key() const;
+
+ virtual bool supports_action(const SupportsActionTestBase &) const PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual void perform_action(Action &) const;
+
+ virtual void invalidate_masks() const;
+ virtual bool breaks_portage() const PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual bool arbitrary_less_than_comparison(const PackageID &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+ virtual std::size_t extra_hash_value() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+ };
+ }
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/unpackaged_repository.cc b/paludis/repositories/unpackaged/unpackaged_repository.cc
new file mode 100644
index 0000000..6dde80d
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_repository.cc
@@ -0,0 +1,140 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/unpackaged_id.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/set.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/package_id.hh>
+#include <paludis/action.hh>
+
+using namespace paludis;
+using namespace paludis::unpackaged_repositories;
+
+#include <paludis/repositories/unpackaged/unpackaged_repository-sr.cc>
+
+namespace paludis
+{
+ template <>
+ struct Implementation<UnpackagedRepository>
+ {
+ const UnpackagedRepositoryParams params;
+ tr1::shared_ptr<const PackageID> id;
+ tr1::shared_ptr<PackageIDSequence> ids;
+ tr1::shared_ptr<QualifiedPackageNameSet> package_names;
+ tr1::shared_ptr<CategoryNamePartSet> category_names;
+
+ Implementation(const RepositoryName & n,
+ const UnpackagedRepositoryParams & p) :
+ params(p),
+ id(new UnpackagedID(params.environment, params.name, params.version, params.slot, n, params.location)),
+ ids(new PackageIDSequence),
+ package_names(new QualifiedPackageNameSet),
+ category_names(new CategoryNamePartSet)
+ {
+ ids->push_back(id);
+ package_names->insert(id->name());
+ category_names->insert(id->name().category);
+ }
+ };
+}
+
+UnpackagedRepository::UnpackagedRepository(const RepositoryName & n,
+ const UnpackagedRepositoryParams & params) :
+ PrivateImplementationPattern<UnpackagedRepository>(new Implementation<UnpackagedRepository>(n, params)),
+ Repository(n, RepositoryCapabilities::create()
+ .installed_interface(0)
+ .sets_interface(0)
+ .syncable_interface(0)
+ .use_interface(0)
+ .world_interface(0)
+ .mirrors_interface(0)
+ .environment_variable_interface(0)
+ .provides_interface(0)
+ .virtuals_interface(0)
+ .make_virtuals_interface(0)
+ .destination_interface(0)
+ .licenses_interface(0)
+ .e_interface(0)
+ .hook_interface(0)
+ .qa_interface(0)
+ .manifest_interface(0),
+ "unpackaged")
+{
+}
+
+UnpackagedRepository::~UnpackagedRepository()
+{
+}
+
+tr1::shared_ptr<const PackageIDSequence>
+UnpackagedRepository::do_package_ids(const QualifiedPackageName & n) const
+{
+ return n == _imp->id->name() ? _imp->ids : make_shared_ptr(new PackageIDSequence);
+}
+
+tr1::shared_ptr<const QualifiedPackageNameSet>
+UnpackagedRepository::do_package_names(const CategoryNamePart & c) const
+{
+ return c == _imp->id->name().category ? _imp->package_names : make_shared_ptr(new QualifiedPackageNameSet);
+}
+
+tr1::shared_ptr<const CategoryNamePartSet>
+UnpackagedRepository::do_category_names() const
+{
+ return _imp->category_names;
+}
+
+tr1::shared_ptr<const CategoryNamePartSet>
+UnpackagedRepository::do_category_names_containing_package(const PackageNamePart & p) const
+{
+ return p == _imp->id->name().package ? _imp->category_names : make_shared_ptr(new CategoryNamePartSet);
+}
+
+bool
+UnpackagedRepository::do_has_package_named(const QualifiedPackageName & q) const
+{
+ return q == _imp->id->name();
+}
+
+bool
+UnpackagedRepository::do_has_category_named(const CategoryNamePart & c) const
+{
+ return c == _imp->id->name().category;
+}
+
+bool
+UnpackagedRepository::do_some_ids_might_support_action(const SupportsActionTestBase & test) const
+{
+ return _imp->id->supports_action(test);
+}
+
+void
+UnpackagedRepository::invalidate()
+{
+ _imp.reset(new Implementation<UnpackagedRepository>(name(), _imp->params));
+}
+
+void
+UnpackagedRepository::invalidate_masks()
+{
+}
+
diff --git a/paludis/repositories/unpackaged/unpackaged_repository.hh b/paludis/repositories/unpackaged/unpackaged_repository.hh
new file mode 100644
index 0000000..71f145d
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_repository.hh
@@ -0,0 +1,74 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_UNPACKAGED_REPOSITORY_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORIES_UNPACKAGED_UNPACKAGED_REPOSITORY_HH 1
+
+#include <paludis/repository.hh>
+#include <paludis/util/map.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+namespace paludis
+{
+ namespace unpackaged_repositories
+ {
+#include <paludis/repositories/unpackaged/unpackaged_repository-sr.hh>
+ }
+
+ class PALUDIS_VISIBLE UnpackagedRepository :
+ private PrivateImplementationPattern<UnpackagedRepository>,
+ public Repository
+ {
+ protected:
+ virtual tr1::shared_ptr<const PackageIDSequence> do_package_ids(
+ const QualifiedPackageName &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual tr1::shared_ptr<const QualifiedPackageNameSet> do_package_names(
+ const CategoryNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual tr1::shared_ptr<const CategoryNamePartSet> do_category_names() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual tr1::shared_ptr<const CategoryNamePartSet> do_category_names_containing_package(
+ const PackageNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool do_has_package_named(const QualifiedPackageName &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool do_has_category_named(const CategoryNamePart &) const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ virtual bool do_some_ids_might_support_action(const SupportsActionTestBase &) const;
+
+ public:
+ UnpackagedRepository(
+ const RepositoryName &,
+ const unpackaged_repositories::UnpackagedRepositoryParams &);
+
+ ~UnpackagedRepository();
+
+ virtual void invalidate();
+ virtual void invalidate_masks();
+ };
+}
+
+#endif
diff --git a/paludis/repositories/unpackaged/unpackaged_repository.sr b/paludis/repositories/unpackaged/unpackaged_repository.sr
new file mode 100644
index 0000000..472e9b9
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_repository.sr
@@ -0,0 +1,16 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et :
+
+make_class_UnpackagedRepositoryParams()
+{
+ visible
+
+ key environment "Environment *"
+ key location FSEntry
+ key name QualifiedPackageName
+ key version VersionSpec
+ key slot SlotName
+
+ allow_named_args
+}
+
diff --git a/paludis/repositories/unpackaged/unpackaged_repository_TEST.cc b/paludis/repositories/unpackaged/unpackaged_repository_TEST.cc
new file mode 100644
index 0000000..6dff5cc
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_repository_TEST.cc
@@ -0,0 +1,207 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 <paludis/repositories/unpackaged/unpackaged_repository.hh>
+#include <paludis/repositories/unpackaged/installed_unpackaged_repository.hh>
+#include <paludis/environments/test/test_environment.hh>
+#include <paludis/package_database.hh>
+#include <paludis/query.hh>
+#include <paludis/package_id.hh>
+#include <paludis/action.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/join.hh>
+#include <paludis/util/iterator.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct MembersTest : TestCase
+ {
+ MembersTest() : TestCase("members") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("unpackaged_repository_TEST_dir/pkg"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageIDSequence> ids(
+ env.package_database()->query(query::All(), qo_order_by_version));
+ TEST_CHECK_EQUAL(join(indirect_iterator(ids->begin()), indirect_iterator(ids->end()), " "),
+ "cat/pkg-1.0:foo::unpackaged");
+ }
+ } test_members;
+
+ struct MetadataTest : TestCase
+ {
+ MetadataTest() : TestCase("metadata") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("unpackaged_repository_TEST_dir/pkg"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageID> id(
+ *env.package_database()->query(query::All(), qo_require_exactly_one)->begin());
+
+ TEST_CHECK_EQUAL(id->version(), VersionSpec("1.0"));
+ TEST_CHECK_EQUAL(id->slot(), SlotName("foo"));
+ TEST_CHECK_EQUAL(id->name(), QualifiedPackageName("cat/pkg"));
+ TEST_CHECK_EQUAL(id->repository()->name(), RepositoryName("unpackaged"));
+ TEST_CHECK(id->fs_location_key());
+ TEST_CHECK_EQUAL(id->fs_location_key()->value(), FSEntry("unpackaged_repository_TEST_dir/pkg"));
+ }
+ } test_metadata;
+
+ struct MasksTest : TestCase
+ {
+ MasksTest() : TestCase("masks") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("unpackaged_repository_TEST_dir/pkg"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ const tr1::shared_ptr<const PackageID> id(
+ *env.package_database()->query(query::All(), qo_require_exactly_one)->begin());
+
+ TEST_CHECK(! id->masked());
+ }
+ } test_masks;
+
+ struct ActionsTest : TestCase
+ {
+ ActionsTest() : TestCase("actions") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ tr1::shared_ptr<Repository> repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("unpackaged_repository_TEST_dir/pkg"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ TEST_CHECK(repo->some_ids_might_support_action(SupportsActionTest<InstallAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<ConfigAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<PretendAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<InfoAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<UninstallAction>()));
+ TEST_CHECK(! repo->some_ids_might_support_action(SupportsActionTest<InstalledAction>()));
+
+ const tr1::shared_ptr<const PackageID> id(
+ *env.package_database()->query(query::All(), qo_require_exactly_one)->begin());
+
+ TEST_CHECK(id->supports_action(SupportsActionTest<InstallAction>()));
+ TEST_CHECK(! id->supports_action(SupportsActionTest<ConfigAction>()));
+ TEST_CHECK(! id->supports_action(SupportsActionTest<PretendAction>()));
+ TEST_CHECK(! id->supports_action(SupportsActionTest<InfoAction>()));
+ TEST_CHECK(! id->supports_action(SupportsActionTest<UninstallAction>()));
+ TEST_CHECK(! id->supports_action(SupportsActionTest<InstalledAction>()));
+ }
+ } test_actions;
+
+ struct InstallTest : TestCase
+ {
+ InstallTest() : TestCase("install") { }
+
+ void run()
+ {
+ TestEnvironment env;
+
+ tr1::shared_ptr<Repository> repo(new UnpackagedRepository(
+ RepositoryName("unpackaged"),
+ unpackaged_repositories::UnpackagedRepositoryParams::create()
+ .environment(&env)
+ .name(QualifiedPackageName("cat/pkg"))
+ .version(VersionSpec("1.0"))
+ .slot(SlotName("foo"))
+ .location(FSEntry("unpackaged_repository_TEST_dir/pkg"))
+ ));
+ env.package_database()->add_repository(1, repo);
+
+ tr1::shared_ptr<Repository> installed_repo(new InstalledUnpackagedRepository(
+ RepositoryName("installed-unpackaged"),
+ unpackaged_repositories::InstalledUnpackagedRepositoryParams::create()
+ .environment(&env)
+ .location(FSEntry("unpackaged_repository_TEST_dir/installed"))
+ .root(FSEntry("unpackaged_repository_TEST_dir/root"))
+ ));
+ env.package_database()->add_repository(0, installed_repo);
+
+ TEST_CHECK(! FSEntry("unpackaged_repository_TEST_dir/root/first").is_regular_file());
+
+ const tr1::shared_ptr<const PackageID> id(
+ *env.package_database()->query(query::All(), qo_require_exactly_one)->begin());
+
+ InstallAction action(InstallActionOptions::create()
+ .no_config_protect(false)
+ .debug_build(iado_none)
+ .checks(iaco_default)
+ .destination(installed_repo)
+ );
+ id->perform_action(action);
+
+ TEST_CHECK(FSEntry("unpackaged_repository_TEST_dir/root/first").is_regular_file());
+ }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+ } test_install;
+}
+
diff --git a/paludis/repositories/unpackaged/unpackaged_repository_TEST_cleanup.sh b/paludis/repositories/unpackaged/unpackaged_repository_TEST_cleanup.sh
new file mode 100755
index 0000000..b6991d9
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_repository_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d unpackaged_repository_TEST_dir ] ; then
+ rm -fr unpackaged_repository_TEST_dir
+else
+ true
+fi
+
diff --git a/paludis/repositories/unpackaged/unpackaged_repository_TEST_setup.sh b/paludis/repositories/unpackaged/unpackaged_repository_TEST_setup.sh
new file mode 100755
index 0000000..0ba4fb2
--- /dev/null
+++ b/paludis/repositories/unpackaged/unpackaged_repository_TEST_setup.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir unpackaged_repository_TEST_dir || exit 1
+cd unpackaged_repository_TEST_dir || exit 1
+
+mkdir -p pkg
+cat <<"END" > pkg/first
+This is the first file.
+END
+
+mkdir -p root
+
+mkdir -p installed/{indices/{categories,packages},data/}
+
diff --git a/src/clients/importare/Makefile.am b/src/clients/importare/Makefile.am
new file mode 100644
index 0000000..a6c4a76
--- /dev/null
+++ b/src/clients/importare/Makefile.am
@@ -0,0 +1,73 @@
+AM_CXXFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src \
+ @PALUDIS_CXXFLAGS@ @PALUDIS_CXXFLAGS_VISIBILITY@
+
+SUBDIRS = .
+
+bin_PROGRAMS = importare
+noinst_PROGRAMS = man-importare
+noinst_DATA = importare.html
+man_MANS = importare.1
+
+importare.1 : man-importare
+ ./man-importare > $@
+
+importare.html : man-importare
+ ./man-importare --html > $@
+
+man_importare_SOURCES = \
+ man_importare.cc \
+ command_line.hh \
+ command_line.cc
+
+man_importare_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)
+
+importare_SOURCES = \
+ command_line.hh command_line.cc \
+ install.hh install.cc \
+ importare.cc
+
+importare_LDADD = \
+ $(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)
+
+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/importare/command_line.cc b/src/clients/importare/command_line.cc
new file mode 100644
index 0000000..2e566ad
--- /dev/null
+++ b/src/clients/importare/command_line.cc
@@ -0,0 +1,91 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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/util/instantiation_policy-impl.hh>
+
+using namespace paludis;
+
+template class paludis::InstantiationPolicy<CommandLine, paludis::instantiation_method::SingletonTag>;
+
+CommandLine::CommandLine() :
+ ArgsHandler(),
+
+ action_args(this, "Actions",
+ "Selects which basic action to perform. Exactly one action should "
+ "be specified."),
+ a_install(&action_args, "install", 'i', "Install one or more packages (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 relevant 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)"),
+
+ source_args(this, "Source options",
+ "Options affecting the source image"),
+ a_location(&source_args, "location", 'l', "Location of source image (default: current directory)"),
+
+ install_args(this, "Install options",
+ "Options which are relevant for --install"),
+
+ dl_args(this)
+{
+ add_usage_line("[ --install ] [ --location path/ ] category/package [ version ] [ slot ]");
+
+ install_args.a_preserve_world.remove();
+ install_args.a_preserve_world.set_specified(true);
+ install_args.a_add_to_world_spec.remove();
+ install_args.a_debug_build.remove();
+ install_args.a_checks.remove();
+ install_args.a_fetch.remove();
+ install_args.a_no_safe_resume.remove();
+
+ dl_args.dl_reinstall_targets.remove();
+}
+
+std::string
+CommandLine::app_name() const
+{
+ return "importare";
+}
+
+std::string
+CommandLine::app_synopsis() const
+{
+ return "The Paludis unpackaged package installer";
+}
+
+std::string
+CommandLine::app_description() const
+{
+ return
+ "importare can be used to manage packages where no real package file is available. It "
+ "treats the contents of a named directory as being the content of the package, and uses "
+ "a dummy package name provided on the command line to do the install. Safe merge, unmerge, "
+ "upgrade and replace support is provided, as is content tracking for installed files.";
+}
+
+CommandLine::~CommandLine()
+{
+}
+
diff --git a/src/clients/importare/command_line.hh b/src/clients/importare/command_line.hh
new file mode 100644
index 0000000..611e46d
--- /dev/null
+++ b/src/clients/importare/command_line.hh
@@ -0,0 +1,62 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_SRC_CLIENTS_IMPORTARE_COMMAND_LINE_HH
+#define PALUDIS_GUARD_SRC_CLIENTS_IMPORTARE_COMMAND_LINE_HH 1
+
+#include <paludis/args/args.hh>
+#include <paludis/util/instantiation_policy.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_install;
+ 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::ArgsGroup source_args;
+ paludis::args::StringArg a_location;
+
+ paludis::args::InstallArgsGroup install_args;
+ paludis::args::DepListArgsGroup dl_args;
+};
+
+#endif
diff --git a/src/clients/importare/importare.cc b/src/clients/importare/importare.cc
new file mode 100644
index 0000000..f4840c4
--- /dev/null
+++ b/src/clients/importare/importare.cc
@@ -0,0 +1,209 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 "install.hh"
+
+#include <paludis/args/do_help.hh>
+#include <src/output/colour.hh>
+
+#include <paludis/environment_maker.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/map.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/about.hh>
+#include <paludis/repository_maker.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+
+#include <algorithm>
+#include <iterator>
+#include <iostream>
+#include <vector>
+
+using namespace paludis;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+namespace
+{
+ struct DoVersion
+ {
+ };
+
+ void display_version()
+ {
+ cout << "importare" << " " << PALUDIS_VERSION_MAJOR << "."
+ << PALUDIS_VERSION_MINOR << "." << PALUDIS_VERSION_MICRO << PALUDIS_VERSION_SUFFIX;
+ if (! std::string(PALUDIS_SUBVERSION_REVISION).empty())
+ cout << " svn " << PALUDIS_SUBVERSION_REVISION;
+ cout << endl;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ Context context("In program " + join(argv, argv + argc, " ") + ":");
+
+ try
+ {
+ CommandLine::get_instance()->run(argc, argv, "importare", "IMPORTARE_OPTIONS", "IMPORTARE_CMDLINE");
+ set_use_colour(! CommandLine::get_instance()->a_no_color.specified());
+
+ if (CommandLine::get_instance()->a_help.specified())
+ throw args::DoHelp();
+
+ if (CommandLine::get_instance()->a_version.specified())
+ throw DoVersion();
+
+ if (CommandLine::get_instance()->a_log_level.specified())
+ Log::get_instance()->set_log_level(CommandLine::get_instance()->a_log_level.option());
+ else
+ Log::get_instance()->set_log_level(ll_qa);
+
+ Log::get_instance()->set_program_name(argv[0]);
+
+ /* need at most one action */
+ if (1 < (
+ CommandLine::get_instance()->a_install.specified()
+ ))
+ throw args::DoHelp("you should specify at most one action");
+
+ std::string paludis_command("paludis"), env_spec;
+
+ if (CommandLine::get_instance()->a_environment.specified())
+ {
+ env_spec = CommandLine::get_instance()->a_environment.argument();
+ 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_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(env_spec));
+ env->set_paludis_command(paludis_command);
+
+ try
+ {
+ std::vector<std::string> params(
+ CommandLine::get_instance()->begin_parameters(),
+ CommandLine::get_instance()->end_parameters());
+
+ if ((params.size() > 3) || (params.size() < 1))
+ throw args::DoHelp("install action takes between one and three parameters (cat/pkg version slot)");
+
+ QualifiedPackageName q(params[0]);
+ VersionSpec v(params.size() >= 2 ? params[1] : "0");
+ SlotName s(params.size() >= 3 ? params[2] : "0");
+
+ tr1::shared_ptr<Map<std::string, std::string> > keys(new Map<std::string, std::string>);
+ keys->insert("location", stringify(
+ CommandLine::get_instance()->a_location.specified() ?
+ FSEntry(CommandLine::get_instance()->a_location.argument()) :
+ FSEntry::cwd()));
+ keys->insert("format", "unpackaged");
+ keys->insert("name", stringify(q));
+ keys->insert("version", stringify(v));
+ keys->insert("slot", stringify(s));
+ tr1::shared_ptr<Repository> repo((*RepositoryMaker::get_instance()->find_maker("unpackaged"))(env.get(), keys));
+ env->package_database()->add_repository(10, repo);
+ tr1::shared_ptr<const PackageIDSequence> ids(repo->package_ids(q));
+ if (1 != std::distance(ids->begin(), ids->end()))
+ throw InternalError(PALUDIS_HERE, "ids is '" + join(indirect_iterator(ids->begin()), indirect_iterator(
+ ids->end()), " ") + "'");
+
+ return do_install(env, *ids->begin());
+ }
+ catch (const NoSuchRepositoryError & e)
+ {
+ cout << endl;
+ cerr << "Unhandled exception:" << endl
+ << " * " << e.backtrace("\n * ")
+ << e.message() << " (" << e.what() << ")" << endl;
+ if (env->package_database()->has_repository_named(RepositoryName("x-" + stringify(e.name()))))
+ cerr << "Perhaps you meant 'x-" << e.name() << "'?" << endl;
+ return EXIT_FAILURE;
+ }
+ }
+ catch (const DoVersion &)
+ {
+ display_version();
+ cout << endl;
+ cout << "Paludis comes with ABSOLUTELY NO WARRANTY. Paludis is free software, and you" << endl;
+ cout << "are welcome to redistribute it under the terms of the GNU General Public" << endl;
+ cout << "License, version 2." << endl;
+
+ return EXIT_SUCCESS;
+ }
+ catch (const paludis::args::ArgsError & e)
+ {
+ cerr << "Usage error: " << e.message() << endl;
+ cerr << "Try " << argv[0] << " --help" << endl;
+ return EXIT_FAILURE;
+ }
+ catch (const args::DoHelp & h)
+ {
+ if (h.message.empty())
+ {
+ cout << "Usage: " << argv[0] << " [options]" << endl;
+ cout << endl;
+ cout << *CommandLine::get_instance();
+ return EXIT_SUCCESS;
+ }
+ else
+ {
+ cerr << "Usage error: " << h.message << endl;
+ cerr << "Try " << argv[0] << " --help" << endl;
+ return EXIT_FAILURE;
+ }
+ }
+ catch (const Exception & e)
+ {
+ cout << endl;
+ cerr << "Unhandled exception:" << endl
+ << " * " << e.backtrace("\n * ")
+ << e.message() << " (" << e.what() << ")" << endl;
+ return EXIT_FAILURE;
+ }
+ catch (const std::exception & e)
+ {
+ cout << endl;
+ cerr << "Unhandled exception:" << endl
+ << " * " << e.what() << endl;
+ return EXIT_FAILURE;
+ }
+ catch (...)
+ {
+ cout << endl;
+ cerr << "Unhandled exception:" << endl
+ << " * Unknown exception type. Ouch..." << endl;
+ return EXIT_FAILURE;
+ }
+}
+
+
diff --git a/src/clients/importare/install.cc b/src/clients/importare/install.cc
new file mode 100644
index 0000000..44076b3
--- /dev/null
+++ b/src/clients/importare/install.cc
@@ -0,0 +1,136 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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 "install.hh"
+#include "command_line.hh"
+#include <src/output/console_install_task.hh>
+#include <paludis/args/do_help.hh>
+
+#include <iostream>
+#include <cstdlib>
+#include <cstring>
+
+#include <paludis/install_task.hh>
+#include <paludis/tasks_exceptions.hh>
+
+#include <paludis/util/log.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/sequence.hh>
+#include <paludis/util/set.hh>
+
+#include <paludis/hook.hh>
+#include <paludis/query.hh>
+#include <paludis/package_id.hh>
+#include <paludis/metadata_key.hh>
+#include <paludis/mask.hh>
+#include <paludis/action.hh>
+
+#include <libwrapiter/libwrapiter_forward_iterator.hh>
+#include <libwrapiter/libwrapiter_output_iterator.hh>
+
+using namespace paludis;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+
+namespace
+{
+ class OurInstallTask :
+ public ConsoleInstallTask
+ {
+ private:
+ tr1::shared_ptr<Environment> _env;
+
+ public:
+ OurInstallTask(tr1::shared_ptr<Environment> env, const DepListOptions & options,
+ 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 &) const
+ {
+ return "";
+ }
+
+ void show_resume_command() const
+ {
+ }
+ };
+}
+
+int
+do_install(const tr1::shared_ptr<Environment> & env, const tr1::shared_ptr<const PackageID> & target)
+{
+ Context context("When performing install action from command line:");
+
+ 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);
+
+ task.add_exact_package(target);
+ task.execute();
+
+ cout << endl;
+
+ return task.exit_status();
+}
+
+
diff --git a/src/clients/importare/install.hh b/src/clients/importare/install.hh
new file mode 100644
index 0000000..27a640f
--- /dev/null
+++ b/src/clients/importare/install.hh
@@ -0,0 +1,31 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 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
+ */
+
+#ifndef PALUDIS_GUARD_SRC_CLIENTS_IMPORTARE_INSTALL_HH
+#define PALUDIS_GUARD_SRC_CLIENTS_IMPORTARE_INSTALL_HH 1
+
+#include <paludis/environment-fwd.hh>
+#include <paludis/package_id-fwd.hh>
+#include <paludis/util/tr1_memory.hh>
+
+int do_install(
+ const paludis::tr1::shared_ptr<paludis::Environment> &,
+ const paludis::tr1::shared_ptr<const paludis::PackageID> & target);
+
+#endif
diff --git a/src/clients/importare/man_importare.cc b/src/clients/importare/man_importare.cc
new file mode 100644
index 0000000..a74af78
--- /dev/null
+++ b/src/clients/importare/man_importare.cc
@@ -0,0 +1,79 @@
+/* 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;
+}
+