[setup - the official Cygwin setup program] branch master, updated. release_2.885

jturney-9JcytcrH/[email protected]
Newsgroups gmane.os.cygwin.cvs.apps
Message-ID <[email protected]>


https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=bb8a17ba89743eeaa17574504395d4a9968edfc8

commit bb8a17ba89743eeaa17574504395d4a9968edfc8
Merge: b7bcb7e b1902cb
Author: Jon Turney <[email protected]>
Date:   Sun Jan 28 15:12:03 2018 +0000

    Merge branch 'topic/libsolv'


Diff:
---
 IniDBBuilderPackage.cc  |  346 ++++++----------
 IniDBBuilderPackage.h   |   32 +-
 Makefile.am             |    9 +-
 PackageSpecification.cc |   18 +
 PackageSpecification.h  |    9 +-
 PickPackageLine.cc      |    1 -
 PickView.cc             |    4 -
 README                  |   28 ++
 bootstrap.sh            |    2 +-
 choose.cc               |  171 ++++++--
 choose.h                |   10 +-
 configure.ac            |    2 +
 confirm.cc              |  148 +++++++
 confirm.h               |   34 ++
 cygpackage.cc           |  143 -------
 cygpackage.h            |   74 ----
 desktop.cc              |    1 -
 download.cc             |   76 ++---
 filemanip.cc            |   18 +-
 filemanip.h             |    1 -
 ini.cc                  |    8 +-
 inilex.ll               |    3 +
 iniparse.yy             |    9 +
 install.cc              |   99 ++---
 libsolv.cc              | 1018 +++++++++++++++++++++++++++++++++++++++++++++++
 libsolv.h               |  276 +++++++++++++
 main.cc                 |    4 +
 package_db.cc           |  301 ++++++++++++--
 package_db.h            |   34 ++-
 package_depends.cc      |   19 +-
 package_depends.h       |    2 +-
 package_meta.cc         |  130 +++++--
 package_meta.h          |   21 +-
 package_version.cc      |  320 ---------------
 package_version.h       |  164 +--------
 prereq.cc               |  245 +++---------
 prereq.h                |   27 +-
 res.rc                  |   42 ++-
 resource.h              |    6 +-
 39 files changed, 2430 insertions(+), 1425 deletions(-)

diff --git a/IniDBBuilderPackage.cc b/IniDBBuilderPackage.cc
index a964ee0..4169634 100644
--- a/IniDBBuilderPackage.cc
+++ b/IniDBBuilderPackage.cc
@@ -22,8 +22,6 @@
 #include "IniParseFeedback.h"
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
-#include "cygpackage.h"
 #include "ini.h"
 // for strtoul
 #include <string.h>
@@ -34,7 +32,7 @@
 using namespace std;
 
 IniDBBuilderPackage::IniDBBuilderPackage (IniParseFeedback const &aFeedback) :
-cp (0), cbpv (), cspv (), currentSpec (0), currentNodeList (0), trust (0), _feedback (aFeedback){}
+currentSpec (0), _feedback (aFeedback){}
 
 IniDBBuilderPackage::~IniDBBuilderPackage()
 {
@@ -67,33 +65,34 @@ IniDBBuilderPackage::buildVersion (const std::string& aVersion)
 }
 
 void
-IniDBBuilderPackage::buildPackage (const std::string& name)
+IniDBBuilderPackage::buildPackage (const std::string& _name)
 {
-#if DEBUG
-  if (cp)
-    {
-      Log (LOG_BABBLE) << "Finished with package " << cp->name << endLog;
-      if (cbpv)
-	{
-	  Log (LOG_BABBLE) << "Version " << cbpv.Canonical_version() << endLog;
-	  Log (LOG_BABBLE) << "Depends:";
-	  dumpPackageDepends (cbpv.depends(), Log (LOG_BABBLE));
-	  Log (LOG_BABBLE) << endLog;
-	}
-    }
-#endif
-  packagedb db;
-  cp = db.findBinary (PackageSpecification(name));
-  if (!cp)
-    {
-      cp = new packagemeta (name);
-      db.packages.insert (packagedb::packagecollection::value_type(cp->name,cp));
-    }
-  cbpv = cygpackage::createInstance (name, package_binary);
-  cspv = packageversion ();
+  process();
+
+  /* Reset for next package */
+  name = _name;
+  message_id = "";
+  message_string = "";
+  categories.clear();
+  replace_versions.clear();
+
+  cbpv.reponame = release;
+  cbpv.version = "";
+  cbpv.vendor = release;
+  cbpv.sdesc = "";
+  cbpv.ldesc = "";
+  cbpv.stability = TRUST_CURR;
+  cbpv.type = package_binary;
+  cbpv.spkg = PackageSpecification();
+  cbpv.spkg_id = packageversion();
+  cbpv.requires = NULL;
+  cbpv.obsoletes = NULL;
+  cbpv.archive = packagesource();
+
   currentSpec = NULL;
   currentNodeList = NULL;
-  trust = TRUST_CURR;
+  dependsNodeList = PackageDepends();
+  obsoletesNodeList = PackageDepends();
 #if DEBUG
   Log (LOG_BABBLE) << "Created package " << name << endLog;
 #endif
@@ -102,20 +101,19 @@ IniDBBuilderPackage::buildPackage (const std::string& name)
 void
 IniDBBuilderPackage::buildPackageVersion (const std::string& version)
 {
-  cbpv.setCanonicalVersion (version);
-  add_correct_version();
+  cbpv.version = version;
 }
 
 void
 IniDBBuilderPackage::buildPackageSDesc (const std::string& theDesc)
 {
-  cbpv.set_sdesc(theDesc);
+  cbpv.sdesc = theDesc;
 }
 
 void
 IniDBBuilderPackage::buildPackageLDesc (const std::string& theDesc)
 {
-  cbpv.set_ldesc(theDesc);
+  cbpv.ldesc = theDesc;
 }
 
 void
@@ -124,21 +122,23 @@ IniDBBuilderPackage::buildPackageInstall (const std::string& path,
                                           char *hash,
                                           hashType type)
 {
-  process_src (*cbpv.source(), path);
-  setSourceSize (*cbpv.source(), size);
+  // set archive path, size, mirror, hash
+  cbpv.archive.set_canonical(path.c_str());
+  cbpv.archive.size = atoi(size.c_str());
+  cbpv.archive.sites.push_back(site(parse_mirror));
 
   switch (type) {
   case hashType::sha512:
-    if (hash && !cbpv.source()->sha512_isSet)
+    if (hash && !cbpv.archive.sha512_isSet)
       {
-        memcpy (cbpv.source()->sha512sum, hash, sizeof(cbpv.source()->sha512sum));
-        cbpv.source()->sha512_isSet = true;
+        memcpy (cbpv.archive.sha512sum, hash, sizeof(cbpv.archive.sha512sum));
+        cbpv.archive.sha512_isSet = true;
       }
     break;
 
   case hashType::md5:
-    if (hash && !cbpv.source()->md5.isSet())
-      cbpv.source()->md5.set((unsigned char *)hash);
+    if (hash && !cbpv.archive.md5.isSet())
+      cbpv.archive.md5.set((unsigned char *)hash);
     break;
 
   case hashType::none:
@@ -152,91 +152,70 @@ IniDBBuilderPackage::buildPackageSource (const std::string& path,
                                          char *hash,
                                          hashType type)
 {
-  packagedb db;
-  /* get an appropriate metadata */
-  csp = db.findSource (PackageSpecification (cbpv.Name()));
-  if (!csp)
-    {
-      /* Copy the existing meta data to a new source package */
-      csp = new packagemeta (*cp);
-      /* delete versions information */
-      csp->versions.clear();
-      csp->desired = packageversion();
-      csp->installed = packageversion();
-      csp->curr = packageversion();
-      csp->exp = packageversion();
-      db.sourcePackages.insert (packagedb::packagecollection::value_type(csp->name,csp));
-    }
-  /* create a source packageversion */
-  cspv = cygpackage::createInstance (cbpv.Name(), package_source);
-  cspv.setCanonicalVersion (cbpv.Canonical_version());
-  set<packageversion>::iterator i=find (csp->versions.begin(),
-    csp->versions.end(), cspv);
-  if (i == csp->versions.end())
-    {
-      csp->add_version (cspv);
-    }
-  else
-    cspv = *i;
-
-  if (!cspv.source()->Canonical())
-    cspv.source()->set_canonical (path.c_str());
-  cspv.source()->sites.push_back(site(parse_mirror));
+  /* When there is a source: line, we invent a package to contain the source,
+     and make it the source package for this package. */
 
-  /* creates the relationship between binary and source packageversions */
-  cbpv.setSourcePackageSpecification (PackageSpecification (cspv.Name()));
-  PackageSpecification &spec = cbpv.sourcePackageSpecification();
-  spec.setOperator (PackageSpecification::Equals);
-  spec.setVersion (cbpv.Canonical_version());
+  /* create a source package version */
+  SolverPool::addPackageData cspv = cbpv;
+  cspv.type = package_source;
+  cspv.requires = NULL;
+  cspv.obsoletes = NULL;
 
-  setSourceSize (*cspv.source(), size);
+  /* set archive path, size, mirror, hash */
+  cspv.archive = packagesource();
+  cspv.archive.set_canonical(path.c_str());
+  cspv.archive.size = atoi(size.c_str());
+  cspv.archive.sites.push_back(site(parse_mirror));
 
   switch (type) {
   case hashType::sha512:
-    if (hash && !cspv.source()->sha512_isSet)
+    if (hash && !cspv.archive.sha512_isSet)
       {
-        memcpy (cspv.source()->sha512sum, hash, sizeof(cspv.source()->sha512sum));
-        cspv.source()->sha512_isSet = true;
+        memcpy (cspv.archive.sha512sum, hash, sizeof(cspv.archive.sha512sum));
+        cspv.archive.sha512_isSet = true;
       }
     break;
 
   case hashType::md5:
-    if (hash && !cspv.source()->md5.isSet())
-      cspv.source()->md5.set((unsigned char *)hash);
+    if (hash && !cspv.archive.md5.isSet())
+      cspv.archive.md5.set((unsigned char *)hash);
     break;
 
   case hashType::none:
     break;
   }
+
+  packagedb db;
+  packageversion spkg_id = db.addSource (name + "-src", cspv);
+
+  /* create relationship between binary and source packageversions */
+  cbpv.spkg = PackageSpecification(name + "-src");
+  cbpv.spkg_id = spkg_id;
 }
 
 void
-IniDBBuilderPackage::buildPackageTrust (int newtrust)
+IniDBBuilderPackage::buildPackageTrust (trusts newtrust)
 {
-  trust = newtrust;
-  if (newtrust != TRUST_UNKNOWN)
-    {
-      cbpv = cygpackage::createInstance (cp->name, package_binary);
-      cspv = packageversion ();
-    }
+  process();
+  cbpv.stability = newtrust;
 }
 
 void
 IniDBBuilderPackage::buildPackageCategory (const std::string& name)
 {
-  cp->add_category (name);
+  categories.insert(name);
 }
 
 void
 IniDBBuilderPackage::buildBeginDepends ()
 {
 #if DEBUG
-  Log (LOG_BABBLE) << "Beginning of a depends statement for " << cp->name
-    << endLog;
+  Log (LOG_BABBLE) << "Beginning of a depends statement " << endLog;
 #endif
   currentSpec = NULL;
-  cbpv.depends()->clear();
-  currentNodeList = cbpv.depends();
+  dependsNodeList = PackageDepends();
+  currentNodeList = &dependsNodeList;
+  cbpv.requires = &dependsNodeList;
 }
 
 void
@@ -246,55 +225,49 @@ IniDBBuilderPackage::buildBeginBuildDepends ()
   Log (LOG_BABBLE) << "Beginning of a Build-Depends statement" << endLog;
 #endif
   currentSpec = NULL;
-  currentNodeList = NULL; /* there is currently nowhere to store Build-Depends information */
+  currentNodeList = NULL;
+  /* there is currently nowhere to store Build-Depends information */
 }
 
 void
-IniDBBuilderPackage::buildSourceName (const std::string& name)
+IniDBBuilderPackage::buildBeginObsoletes ()
 {
-  if (cbpv)
-    {
-      cbpv.setSourcePackageSpecification (PackageSpecification (name));
 #if DEBUG
-      Log (LOG_BABBLE) << "\"" << cbpv.sourcePackageSpecification() <<
-	"\" is the source package for " << cp->name << "." << endLog;
+  Log (LOG_BABBLE) << "Beginning of an obsoletes statement" << endLog;
 #endif
-    }
-  else
-      _feedback.warning ((std::string ("Attempt to set source for package")
-                          + std::string(cp->name)
-			  + "before creation of a version.").c_str());
+  currentSpec = NULL;
+  obsoletesNodeList = PackageDepends();
+  currentNodeList = &obsoletesNodeList;
+  cbpv.obsoletes = &obsoletesNodeList;
 }
 
 void
-IniDBBuilderPackage::buildSourceNameVersion (const std::string& version)
+IniDBBuilderPackage::buildSourceName (const std::string& _name)
 {
-  if (cbpv)
-    {
-      cbpv.sourcePackageSpecification().setOperator (PackageSpecification::Equals);
-      cbpv.sourcePackageSpecification().setVersion (version);
+  // When there is a Source: line, that names a real source package
+  packagedb db;
+  cbpv.spkg = PackageSpecification(_name);
+  cbpv.spkg_id = db.findSourceVersion (PackageSpecification(_name, cbpv.version));
 #if DEBUG
-      Log (LOG_BABBLE) << "The source version needed for " << cp->name <<
-	" is " << version << "." << endLog;
+  Log (LOG_BABBLE) << "\"" << _name << "\" is the source package for " << name << "." << endLog;
 #endif
-    }
-  else
-      _feedback.warning ((std::string ("Attempt to set source version for package")
-                          + std::string(cp->name)
-			  + "before creation of a version.").c_str());
+}
+
+void
+IniDBBuilderPackage::buildSourceNameVersion (const std::string& version)
+{
+  // XXX: should be stored as sourceevr
 }
 
 void
 IniDBBuilderPackage::buildPackageListNode (const std::string & name)
 {
-  if (currentNodeList)
-    {
 #if DEBUG
-      Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog;
+  Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog;
 #endif
-      currentSpec = new PackageSpecification (name);
-      currentNodeList->push_back (currentSpec);
-    }
+  currentSpec = new PackageSpecification (name);
+  if (currentNodeList)
+    currentNodeList->push_back (currentSpec);
 }
 
 void
@@ -308,13 +281,8 @@ IniDBBuilderPackage::buildPackageListOperator (PackageSpecification::_operators
 	endLog;
 #endif
     }
-  else
-    _feedback.warning ((std::string ("Attempt to set an operator for package ")
-                        + std::string(cp->name)
-		       + " with no current specification.").c_str());
 }
 
-
 void
 IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersion)
 {
@@ -326,110 +294,50 @@ IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersio
 	endLog;
 #endif
     }
-  else
-      _feedback.warning ((std::string ("Attempt to set an operator version for package ")
-                          + std::string(cp->name)
-			  + " with no current specification.").c_str());
 }
 
-/* privates */
-
 void
-IniDBBuilderPackage::add_correct_version()
+IniDBBuilderPackage::buildMessage (const std::string& _message_id, const std::string& _message_string)
 {
-  if (currentNodeList)
-    *cbpv.depends() = *currentNodeList;
-
-  int merged = 0;
-  for (set<packageversion>::iterator n = cp->versions.begin();
-       !merged && n != cp->versions.end(); ++n)
-    if (*n == cbpv )
-      {
-	packageversion ver = *n;
-        /* ASSUMPTIONS:
-           categories and requires are consistent for the same version across
-           all mirrors
-           */
-        /*
-          XXX: if the versions are equal but the size/md5sum are different,
-          we should alert the user, as they may not be getting what they expect...
-        */
-        /* Copy the binary mirror across if this site claims to have an install */
-        if (cbpv.source()->sites.size() )
-          ver.source()->sites.push_back(site (cbpv.source()->sites.begin()->key));
-        /* Copy the descriptions across */
-        if (cbpv.SDesc ().size() && !n->SDesc ().size())
-          ver.set_sdesc (cbpv.SDesc ());
-        if (cbpv.LDesc ().size() && !n->LDesc ().size())
-          ver.set_ldesc (cbpv.LDesc ());
-	if (cbpv.depends()->size() && !ver.depends ()->size())
-	  *ver.depends() = *cbpv.depends();
-	/* TODO: other package lists */
-	/* Prevent dangling references */
-	currentNodeList = NULL;
-	currentSpec = NULL;
-        cbpv = *n;
-        merged = 1;
-#if DEBUG
-        Log (LOG_BABBLE) << cp->name << " merged with an existing version " << cbpv.Canonical_version() << endLog;
-#endif
-      }
-
-  if (!merged)
-    {
-      cp->add_version (cbpv);
-#if DEBUG
-      Log (LOG_BABBLE) << cp->name << " version " << cbpv.Canonical_version() << " added" << endLog;
-#endif
-    }
-
-  /*
-    Should this version be the one selected for this package at a given
-    stability/trust setting?  After merging potentially multiple package
-    databases, we should pick the one with the highest version number.
-  */
-  packageversion *v = NULL;
-  switch (trust)
-  {
-    case TRUST_CURR:
-      v = &(cp->curr);
-    break;
-    case TRUST_TEST:
-      v = &(cp->exp);
-    break;
-  }
-
-  if (v)
-    {
-      int comparison = packageversion::compareVersions(cbpv, *v);
-
-      if ((bool)(*v))
-        Log (LOG_BABBLE) << "package " << cp->name << " comparing versions " << cbpv.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog;
-
-      if (comparison > 0)
-        {
-          *v = cbpv;
-        }
-    }
+  message_id = _message_id;
+  message_string = _message_string;
 }
 
 void
-IniDBBuilderPackage::process_src (packagesource &src, const std::string& path)
+IniDBBuilderPackage::buildPackageReplaceVersionsList (const std::string& version)
 {
-  if (!src.Canonical())
-    src.set_canonical (path.c_str());
-  src.sites.push_back(site(parse_mirror));
+  replace_versions.insert(version);
 }
 
+/* privates */
 void
-IniDBBuilderPackage::setSourceSize (packagesource &src, const std::string& size)
+IniDBBuilderPackage::process ()
 {
-  if (!src.size)
-    src.size = atoi(size.c_str());
-}
+  if (!name.size())
+    return;
 
-void
-IniDBBuilderPackage::buildMessage (const std::string& message_id, const std::string& message)
-{
-  cp->set_message (message_id, message);
+#if DEBUG
+  Log (LOG_BABBLE) << "Finished with package " << name << endLog;
+  Log (LOG_BABBLE) << "Version " << cbpv.version << endLog;
+#endif
+
+  /* Transfer the accumulated package information to packagedb */
+  packagedb db;
+  packagemeta *pkg = db.addBinary (name, cbpv);
+
+  // For no good historical reason, some data lives in packagemeta rather than
+  // the packageversion
+  for (auto i = categories.begin(); i != categories.end(); i++)
+    {
+      pkg->add_category(*i);
+    }
+  pkg->set_message(message_id, message_string);
+  pkg->set_version_blacklist(replace_versions);
+
+  // Reset for next version
+  cbpv.version = "";
+  cbpv.type = package_binary;
+  cbpv.spkg = PackageSpecification();
+  cbpv.spkg_id = packageversion();
+  cbpv.archive = packagesource();
 }
diff --git a/IniDBBuilderPackage.h b/IniDBBuilderPackage.h
index 323b186..da2d97d 100644
--- a/IniDBBuilderPackage.h
+++ b/IniDBBuilderPackage.h
@@ -17,11 +17,15 @@
 #define SETUP_INIDBBUILDERPACKAGE_H
 
 #include <vector>
-#include "package_version.h"
+#include <set>
+
+#include "package_message.h"
+#include "PackageTrust.h"
+#include "String++.h"
+#include "libsolv.h"
 
 class IniParseFeedback;
 class packagesource;
-class packagemeta;
 
 enum class hashType { none, md5, sha512 };
 
@@ -42,21 +46,24 @@ public:
   void buildPackageSource (const std::string&, const std::string&,
                            char *, hashType);
 
-  void buildPackageTrust (int);
+  void buildPackageTrust (trusts);
   void buildPackageCategory (const std::string& );
 
   void buildBeginDepends ();
   void buildBeginBuildDepends ();
+  void buildBeginObsoletes ();
   void buildMessage (const std::string&, const std::string&);
   void buildSourceName (const std::string& );
   void buildSourceNameVersion (const std::string& );
   void buildPackageListNode (const std::string& );
   void buildPackageListOperator (PackageSpecification::_operators const &);
   void buildPackageListOperatorVersion (const std::string& );
+  void buildPackageReplaceVersionsList (const std::string& );
 
   void set_arch (const std::string& a) { arch = a; }
   void set_release (const std::string& rel) { release = rel; }
 
+  // setup.ini header data
   unsigned int timestamp;
   std::string arch;
   std::string release;
@@ -64,17 +71,20 @@ public:
   std::string parse_mirror;
 
 private:
-  void add_correct_version();
-  void process_src (packagesource &src, const std::string& );
-  void setSourceSize (packagesource &src, const std::string& );
+  void process ();
 
-  packagemeta *cp;
-  packageversion cbpv;
-  packagemeta *csp;
-  packageversion cspv;
+  // package data
+  std::string name;
+  std::set <std::string, casecompare_lt_op> categories;
+  std::string message_id;
+  std::string message_string;
   PackageSpecification *currentSpec;
   PackageDepends *currentNodeList;
-  int trust;
+  PackageDepends dependsNodeList;
+  PackageDepends obsoletesNodeList;
+  SolverPool::addPackageData cbpv;
+  std::set <std::string> replace_versions;
+
   IniParseFeedback const &_feedback;
 };
 
diff --git a/Makefile.am b/Makefile.am
index a238d88..37341b9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -98,7 +98,7 @@ inilint_SOURCES = \
 	String++.h
 
 @SETUP@_LDADD = \
-	libgetopt++/libgetopt++.la -lgcrypt -lgpg-error -llzma -lbz2 -lz \
+	libgetopt++/libgetopt++.la -lgcrypt -lgpg-error -llzma -lbz2 -lz -lsolv -lregex \
 	-lshlwapi -lcomctl32 -lole32 -lws2_32 -lpsapi -luuid -lntdll -lwininet -lmingw32
 @SETUP@_LDFLAGS = -mwindows -Wc,-static -static-libtool-libs
 @SETUP@_SOURCES = \
@@ -119,6 +119,8 @@ inilint_SOURCES = \
 	compress_gz.h \
 	compress_xz.cc \
 	compress_xz.h \
+	confirm.cc \
+	confirm.h \
 	ConnectionSetting.cc \
 	ConnectionSetting.h \
 	ControlAdjuster.cc \
@@ -126,8 +128,6 @@ inilint_SOURCES = \
 	crypto.cc \
 	crypto.h \
 	cyg-pubkey.h \
-	cygpackage.cc \
-	cygpackage.h \
 	desktop.cc \
 	desktop.h \
 	dialog.cc \
@@ -171,6 +171,8 @@ inilint_SOURCES = \
 	IOStreamProvider.h \
 	KeysSetting.cc \
 	KeysSetting.h \
+	libsolv.cc \
+	libsolv.h \
 	localdir.cc \
 	localdir.h \
 	LogFile.cc \
@@ -206,7 +208,6 @@ inilint_SOURCES = \
 	package_meta.h \
 	package_source.cc \
 	package_source.h \
-	package_version.cc \
 	package_version.h \
 	PackageSpecification.cc \
 	PackageSpecification.h \
diff --git a/PackageSpecification.cc b/PackageSpecification.cc
index 247f3da..b58ad80 100644
--- a/PackageSpecification.cc
+++ b/PackageSpecification.cc
@@ -22,12 +22,30 @@ PackageSpecification::PackageSpecification (const std::string& packageName)
 {
 }
 
+PackageSpecification::PackageSpecification (const std::string& packageName,
+                                            const std::string& packageVersion)
+  : _packageName (packageName) , _operator (Equals), _version (packageVersion)
+{
+}
+
 const std::string&
 PackageSpecification::packageName () const
 {
   return _packageName;
 }
 
+const PackageSpecification::_operators
+PackageSpecification::op() const
+{
+  return _operator;
+}
+
+const std::string&
+PackageSpecification::version() const
+{
+  return _version;
+}
+
 void
 PackageSpecification::setOperator (_operators anOperator)
 {
diff --git a/PackageSpecification.h b/PackageSpecification.h
index 4c3ed6d..b881494 100644
--- a/PackageSpecification.h
+++ b/PackageSpecification.h
@@ -18,7 +18,9 @@
 
 #include <iosfwd>
 #include "String++.h"
-class packageversion;
+
+class SolvableVersion;
+typedef SolvableVersion packageversion;
 
 /* Describe a package - i.e. we need version 5 of apt */
 
@@ -27,6 +29,8 @@ class PackageSpecification
 public:
   PackageSpecification () : _packageName (), _operator(Equals) {}
   PackageSpecification (const std::string& packageName);
+  PackageSpecification (const std::string& packageName,
+                        const std::string &packageVersion);
   ~PackageSpecification () {}
 
   enum _operators
@@ -39,6 +43,9 @@ public:
   };
 
   const std::string& packageName() const;
+  const _operators op() const;
+  const std::string& version() const;
+
   void setOperator (_operators);
   void setVersion (const std::string& );
 
diff --git a/PickPackageLine.cc b/PickPackageLine.cc
index 31103ce..2caeafe 100644
--- a/PickPackageLine.cc
+++ b/PickPackageLine.cc
@@ -16,7 +16,6 @@
 #include "PickPackageLine.h"
 #include "PickView.h"
 #include "package_db.h"
-#include "package_version.h"
 
 void
 PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int show_cat)
diff --git a/PickView.cc b/PickView.cc
index 2e1beda..c583eea 100644
--- a/PickView.cc
+++ b/PickView.cc
@@ -21,7 +21,6 @@
 #include "PickPackageLine.h"
 #include "PickCategoryLine.h"
 #include "package_db.h"
-#include "package_version.h"
 #include "dialog.h"
 #include "resource.h"
 /* For 'source' */
@@ -944,9 +943,6 @@ PickView::defaultTrust (trusts trust)
 {
   this->deftrust = trust;
 
-  packagedb db;
-  db.defaultTrust(trust);
-
   // force the picker to redraw
   RECT r = GetClientRect ();
   InvalidateRect (this->GetHWND(), &r, TRUE);
diff --git a/README b/README
index 120049c..9be9c50 100644
--- a/README
+++ b/README
@@ -3,6 +3,10 @@ for the Cygwin net releases.
 
 HOW TO BUILD:
 -------------
+
+Cygwin
+------
+
 Setup should build out-of-the-box on any Cygwin environment that has all the
 required packages and their dependencies installed:
 
@@ -10,6 +14,7 @@ required packages and their dependencies installed:
   - mingw64-${arch}-headers
   - mingw64-${arch}-gcc-g++
   - mingw64-${arch}-libgcrypt
+  - mingw64-${arch}-libsolv
   - mingw64-${arch}-bzip2
   - mingw64-${arch}-xz
   - mingw64-${arch}-zlib
@@ -26,6 +31,29 @@ to make changes to the build system.
   - flex
   - bison
 
+Fedora
+------
+
+Setup should also build out-of-the-box in a Fedora environment that has all the
+required packages and their dependencies installed:
+
+  - make
+  - libtool
+  - mingw${arch}-gcc-c++
+  - mingw${arch}-zlib-static
+  - mingw${arch}-libgcrypt-static
+  - mingw${arch}-libgnurx-static (*)
+  - mingw${arch}-libsolv-static
+  - mingw${arch}-bzip2-static
+  - mingw${arch}-xz-libs-static
+  - mingw${arch}-winpthreads-static
+  - upx (optional)
+
+The ${arch} needs to be replaced with either "32" or "64"
+depending on the target architecture to build for.
+
+(*) Requires 'dnf copr enable jturney/mingw-libsolv'
+
 Build commands:
 
 0) If building from git, obtain this project's code:
diff --git a/bootstrap.sh b/bootstrap.sh
index f21206d..1d388da 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -21,7 +21,7 @@ bootstrap() {
 cd "$srcdir"
 
 # Make sure we are running in the right directory
-if [ ! -f cygpackage.cc ]; then
+if [ ! -f main.cc ]; then
   echo "You must run this script from the directory containing it"
   exit 1
 fi
diff --git a/choose.cc b/choose.cc
index a676a64..9cf3a50 100644
--- a/choose.cc
+++ b/choose.cc
@@ -48,7 +48,6 @@
 
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
 
 #include "threebar.h"
 #include "Generic.h"
@@ -64,7 +63,6 @@ static BoolOption UpgradeAlsoOption (false, 'g', "upgrade-also", "Also upgrade i
 static BoolOption CleanOrphansOption (false, 'o', "delete-orphans", "Remove orphaned packages");
 static BoolOption ForceCurrentOption (false, 'f', "force-current", "Select the current version for all packages");
 static BoolOption PruneInstallOption (false, 'Y', "prune-install", "Prune the installation to only the requested packages");
-static BoolOption MirrorOption (false, 'm', "mirror-mode", "Skip package availability check when installing from local directory (requires local directory to be clean mirror!)");
 
 using namespace std;
 
@@ -79,7 +77,8 @@ static ControlAdjuster::ControlInfo ChooserControlsInfo[] = {
   {IDC_CHOOSE_SEARCH_LABEL, 	CP_LEFT,    CP_TOP},
   {IDC_CHOOSE_SEARCH_EDIT,	CP_LEFT,    CP_TOP},
   {IDC_CHOOSE_KEEP, 		CP_RIGHT,   CP_TOP},
-  {IDC_CHOOSE_CURR, 		CP_RIGHT,   CP_TOP},
+  {IDC_CHOOSE_BEST, 		CP_RIGHT,   CP_TOP},
+  {IDC_CHOOSE_SYNC, 		CP_RIGHT,   CP_TOP},
   {IDC_CHOOSE_EXP, 		CP_RIGHT,   CP_TOP},
   {IDC_CHOOSE_VIEW, 		CP_LEFT,    CP_TOP},
   {IDC_LISTVIEW_POS, 		CP_RIGHT,   CP_TOP},
@@ -91,7 +90,7 @@ static ControlAdjuster::ControlInfo ChooserControlsInfo[] = {
 
 ChooserPage::ChooserPage () :
   cmd_show_set (false), saved_geom (false), saw_geom_change (false),
-  timer_id (DEFAULT_TIMER_ID)
+  timer_id (DEFAULT_TIMER_ID), activated (false)
 {
   sizeProcessor.AddControlInfo (ChooserControlsInfo);
 
@@ -153,13 +152,34 @@ ChooserPage::createListview ()
   chooser->setViewMode (!is_new_install || UpgradeAlsoOption || hasManualSelections ?
 			PickView::views::PackagePending : PickView::views::Category);
   SendMessage (GetDlgItem (IDC_CHOOSE_VIEW), CB_SETCURSEL, (WPARAM)chooser->getViewMode(), 0);
-
-  /* FIXME: do we need to init the desired fields ? */
-  static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_CURR, IDC_CHOOSE_EXP, 0 };
-  rbset (GetHWND (), ta, IDC_CHOOSE_CURR);
   ClearBusy ();
 }
 
+void
+ChooserPage::initialUpdateState()
+{
+  // set the initial update state
+  if (ForceCurrentOption)
+    {
+      update_mode_id = IDC_CHOOSE_SYNC;
+      changeTrust(update_mode_id, false, true);
+    }
+  else if (hasManualSelections && !UpgradeAlsoOption)
+    {
+      // if packages are added or removed on the command-line and --upgrade-also
+      // isn't used, we keep the current versions of everything else
+      update_mode_id = IDC_CHOOSE_KEEP;
+    }
+  else
+    {
+      update_mode_id = IDC_CHOOSE_BEST;
+      changeTrust (update_mode_id, false, true);
+    }
+
+  static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_BEST, IDC_CHOOSE_SYNC, 0 };
+  rbset (GetHWND (), ta, update_mode_id);
+}
+
 /* TODO: review ::overrides for possible consolidation */
 void
 ChooserPage::getParentRect (HWND parent, HWND child, RECT * r)
@@ -248,14 +268,30 @@ ChooserPage::OnInit ()
       SendMessage(viewlist, CB_ADDSTRING, 0, (LPARAM)PickView::mode_caption((PickView::views)view));
     }
 
-  SetBusy ();
-  if (source == IDC_SOURCE_DOWNLOAD || source == IDC_SOURCE_LOCALDIR)
-    packagemeta::ScanDownloadedFiles (MirrorOption);
+  if (source == IDC_SOURCE_DOWNLOAD)
+    setPrompt("Select packages to download ");
+  else
+    setPrompt("Select packages to install ");
 
-  packagedb db;
-  db.setExistence ();
-  db.fillMissingCategory ();
+  createListview ();
+
+  AddTooltip (IDC_CHOOSE_KEEP, IDS_TRUSTKEEP_TOOLTIP);
+  AddTooltip (IDC_CHOOSE_BEST, IDS_TRUSTCURR_TOOLTIP);
+  AddTooltip (IDC_CHOOSE_SYNC, IDS_TRUSTSYNC_TOOLTIP);
+  AddTooltip (IDC_CHOOSE_EXP, IDS_TRUSTEXP_TOOLTIP);
+  AddTooltip (IDC_CHOOSE_VIEW, IDS_VIEWBUTTON_TOOLTIP);
+  AddTooltip (IDC_CHOOSE_HIDE, IDS_HIDEOBS_TOOLTIP);
+  AddTooltip (IDC_CHOOSE_SEARCH_EDIT, IDS_SEARCH_TOOLTIP);
 
+  /* Set focus to search edittext control. */
+  PostMessage (GetHWND (), WM_NEXTDLGCTL,
+               (WPARAM) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT), TRUE);
+}
+
+void
+ChooserPage::applyCommandLinePackageSelection()
+{
+  packagedb db;
   for (packagedb::packagecollection::iterator i = db.packages.begin ();
        i != db.packages.end (); ++i)
     {
@@ -264,8 +300,7 @@ ChooserPage::OnInit ()
       bool deleted   = pkg.isManuallyDeleted();
       bool basemisc  = (pkg.categories.find ("Base") != pkg.categories.end ()
 		     || pkg.categories.find ("Orphaned") != pkg.categories.end ());
-      bool upgrade   = wanted || (!pkg.installed && basemisc)
-		     || UpgradeAlsoOption || !hasManualSelections;
+      bool upgrade   = wanted || (!pkg.installed && basemisc);
       bool install   = wanted  && !deleted && !pkg.installed;
       bool reinstall = (wanted  || basemisc) && deleted;
       bool uninstall = (!(wanted  || basemisc) && (deleted || PruneInstallOption))
@@ -276,38 +311,36 @@ ChooserPage::OnInit ()
 	pkg.set_action (packagemeta::Reinstall_action, pkg.curr);
       else if (uninstall)
 	pkg.set_action (packagemeta::Uninstall_action, packageversion ());
-      else if (PruneInstallOption || ForceCurrentOption)
+      else if (PruneInstallOption)
 	pkg.set_action (packagemeta::Default_action, pkg.curr);
       else if (upgrade)
 	pkg.set_action (packagemeta::Default_action, pkg.trustp(true, TRUST_UNKNOWN));
       else
 	pkg.set_action (packagemeta::Default_action, pkg.installed);
     }
-
-  ClearBusy ();
-
-  if (source == IDC_SOURCE_DOWNLOAD)
-    setPrompt("Select packages to download ");
-  else
-    setPrompt("Select packages to install ");
-  createListview ();
-
-  AddTooltip (IDC_CHOOSE_KEEP, IDS_TRUSTKEEP_TOOLTIP);
-  AddTooltip (IDC_CHOOSE_CURR, IDS_TRUSTCURR_TOOLTIP);
-  AddTooltip (IDC_CHOOSE_EXP, IDS_TRUSTEXP_TOOLTIP);
-  AddTooltip (IDC_CHOOSE_VIEW, IDS_VIEWBUTTON_TOOLTIP);
-  AddTooltip (IDC_CHOOSE_HIDE, IDS_HIDEOBS_TOOLTIP);
-  AddTooltip (IDC_CHOOSE_SEARCH_EDIT, IDS_SEARCH_TOOLTIP);
-
-  /* Set focus to search edittext control. */
-  PostMessage (GetHWND (), WM_NEXTDLGCTL,
-	       (WPARAM) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT), TRUE);
 }
 
 void
 ChooserPage::OnActivate()
 {
-  chooser->refresh();;
+  SetBusy();
+
+  packagedb db;
+  db.prep();
+
+  if (!activated)
+    {
+      // Do things which should only happen once, but rely on packagedb being
+      // ready to use, so OnInit() is too early
+      applyCommandLinePackageSelection();
+      initialUpdateState();
+
+      activated = true;
+    }
+
+  ClearBusy();
+
+  chooser->refresh();
   PlaceDialog (true);
 }
 
@@ -360,6 +393,7 @@ ChooserPage::OnBack ()
 void
 ChooserPage::keepClicked()
 {
+  update_mode_id = IDC_CHOOSE_KEEP;
   packagedb db;
   for (packagedb::packagecollection::iterator i = db.packages.begin ();
         i != db.packages.end (); ++i)
@@ -372,12 +406,57 @@ ChooserPage::keepClicked()
 }
 
 void
-ChooserPage::changeTrust(trusts aTrust)
+ChooserPage::changeTrust(int button, bool test, bool initial)
 {
   SetBusy ();
-  chooser->defaultTrust (aTrust);
+
+  update_mode_id = button;
+
+  SolverSolution::updateMode mode;
+  switch (button)
+    {
+    default:
+    case IDC_CHOOSE_KEEP:
+      mode = SolverSolution::keep;
+      break;
+
+    case IDC_CHOOSE_BEST:
+      mode = SolverSolution::updateBest;
+      break;
+
+    case IDC_CHOOSE_SYNC:
+      mode = SolverSolution::updateForce;
+      break;
+    }
+
+  packagedb db;
+  SolverTasks q;
+
+  // usually we want to apply the solver to an empty task list to get the list
+  // of packages to upgrade (if any)
+  if (initial)
+    {
+      // but initially we want a task list with any package changes caused by
+      // command line options
+      // (also note the installed version to avoid generating spurious taskKeep
+      // or taskSkip tasks)
+      for (packagedb::packagecollection::iterator p = db.packages.begin ();
+           p != db.packages.end (); ++p)
+        {
+          packagemeta *pkg = p->second;
+          pkg->default_version = pkg->installed;
+        }
+      q.setTasks();
+    }
+  db.defaultTrust(q, mode, test);
+
+  // configure PickView so 'test' or 'curr' version is chosen when an
+  // uninstalled package is first clicked on.
+  chooser->defaultTrust (test ? TRUST_TEST : TRUST_CURR);
+
   chooser->refresh();
-  PrereqChecker::setTrust (aTrust);
+
+  PrereqChecker::setTestPackages(test);
   ClearBusy ();
 }
 
@@ -444,14 +523,18 @@ ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
         keepClicked();
       break;
 
-    case IDC_CHOOSE_CURR:
+    case IDC_CHOOSE_BEST:
       if (IsButtonChecked (id))
-        changeTrust (TRUST_CURR);
+        changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
       break;
 
-    case IDC_CHOOSE_EXP:
+    case IDC_CHOOSE_SYNC:
       if (IsButtonChecked (id))
-        changeTrust (TRUST_TEST);
+        changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false);
+      break;
+
+    case IDC_CHOOSE_EXP:
+      changeTrust(update_mode_id, IsButtonChecked (id), false);
       break;
 
     case IDC_CHOOSE_HIDE:
diff --git a/choose.h b/choose.h
index 46f0f35..32a1650 100644
--- a/choose.h
+++ b/choose.h
@@ -54,11 +54,13 @@ private:
   RECT getDefaultListViewSize();
   void getParentRect (HWND parent, HWND child, RECT * r);
   void keepClicked();
-  void changeTrust(trusts aTrust);
+  void changeTrust(int button, bool test, bool initial);
   void logOnePackageResult(packagemeta const *aPkg);
   void logResults();
   void setPrompt(char const *aPrompt);
   void PlaceDialog (bool);
+  void applyCommandLinePackageSelection();
+  void initialUpdateState();
 
   PickView *chooser;
   static HWND ins_dialog;
@@ -73,10 +75,8 @@ private:
     WINDOWPLACEMENT wp;
     UINT wpi[sizeof (WINDOWPLACEMENT) / sizeof (UINT)];
   };
-
-
-
-
+  int update_mode_id;
+  bool activated;
 };
 
 #endif /* SETUP_CHOOSE_H */
diff --git a/configure.ac b/configure.ac
index 2c610ed..dcab4ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,8 @@ AC_CHECK_HEADER(zlib.h, , missing_deps="$missing_deps zlib")
 AC_CHECK_HEADER(bzlib.h, , missing_deps="$missing_deps libbz2")
 AC_CHECK_HEADER(lzma.h, , missing_deps="$missing_deps liblzma")
 AC_CHECK_HEADER(gcrypt.h, , missing_deps="$missing_deps libgcrypt")
+AC_CHECK_HEADER(solv/pool.h, , missing_deps="$missing_deps libsolv")
+AC_CHECK_HEADER(regex.h, , missing_deps="$missing_deps libregex")
 
 if test -n "$missing_deps"; then
 	AC_MSG_ERROR([missing prerequisites: $missing_deps])
diff --git a/confirm.cc b/confirm.cc
new file mode 100644
index 0000000..7e949d8
--- /dev/null
+++ b/confirm.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2018
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#include "confirm.h"
+#include "threebar.h"
+#include "resource.h"
+#include "state.h"
+#include "ControlAdjuster.h"
+#include "package_db.h"
+#include "package_meta.h"
+
+extern ThreeBarProgressPage Progress;
+
+// Sizing information.
+static ControlAdjuster::ControlInfo ConfirmControlsInfo[] = {
+  {IDC_CONFIRM_EDIT, CP_STRETCH, CP_STRETCH},
+  {0, CP_LEFT, CP_TOP}
+};
+
+// ---------------------------------------------------------------------------
+// implements class ConfirmPage
+// ---------------------------------------------------------------------------
+
+ConfirmPage::ConfirmPage ()
+{
+  sizeProcessor.AddControlInfo (ConfirmControlsInfo);
+}
+
+bool
+ConfirmPage::Create ()
+{
+  return PropertyPage::Create (IDD_CONFIRM);
+}
+
+void
+ConfirmPage::OnInit ()
+{
+  // set the edit-area to a larger font
+  SetDlgItemFont(IDC_CONFIRM_EDIT, "MS Shell Dlg", 10);
+}
+
+void
+ConfirmPage::OnActivate()
+{
+  // generate a report on actions we're going to take
+  std::string s = "";
+
+  packagedb db;
+  const SolverTransactionList & trans = db.solution.transactions ();
+
+  // first list things we will erase
+  if (source != IDC_SOURCE_DOWNLOAD)
+    {
+      for (SolverTransactionList::const_iterator i = trans.begin ();
+           i != trans.end (); i++)
+        {
+          if (i->type == SolverTransaction::transErase)
+            {
+              packageversion pv = i->version;
+              packagemeta *pkg = db.findBinary (PackageSpecification (pv.Name ()));
+
+              s += "Uninstall ";
+              s += i->version.Name();
+              s += " ";
+              s += i->version.Canonical_version();
+              if (pkg->desired)
+                s += " (automatically added)";
+              s += "\r\n";
+            }
+        }
+    }
+
+  // then list things downloaded or installed
+  for (SolverTransactionList::const_iterator i = trans.begin ();
+       i != trans.end (); i++)
+    {
+      packageversion pv = i->version;
+      packagemeta *pkg = db.findBinary (PackageSpecification (pv.Name ()));
+
+      if (i->type == SolverTransaction::transInstall)
+          {
+            if (source != IDC_SOURCE_DOWNLOAD)
+              s += "Install ";
+            else
+              s += "Download ";
+            s += i->version.Name();
+            s += " ";
+            s += i->version.Canonical_version();
+            if (!pkg->desired)
+              s += " (automatically added)";
+            s += "\r\n";
+          }
+    }
+
+  // be explicit about doing nothing
+  if (s.empty())
+    s += "No changes";
+
+  SetDlgItemText (GetHWND (), IDC_CONFIRM_EDIT, s.c_str ());
+
+  // move focus to 'next' button, so enter doesn't get eaten by edit control
+  HWND nextButton = ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */);
+  PostMessage (GetHWND (), WM_NEXTDLGCTL, (WPARAM)nextButton, TRUE);
+}
+
+long
+ConfirmPage::OnNext ()
+{
+  return whatNext();
+}
+
+long
+ConfirmPage::whatNext ()
+{
+  if (source == IDC_SOURCE_LOCALDIR)
+    {
+      // Next, install
+      Progress.SetActivateTask (WM_APP_START_INSTALL);
+    }
+  else
+    {
+      // Next, start download from internet
+      Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
+    }
+  return IDD_INSTATUS;
+}
+
+long
+ConfirmPage::OnBack ()
+{
+  return IDD_CHOOSE;
+}
+
+long
+ConfirmPage::OnUnattended ()
+{
+  return whatNext();
+}
diff --git a/confirm.h b/confirm.h
new file mode 100644
index 0000000..fe11ee0
--- /dev/null
+++ b/confirm.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#ifndef SETUP_CONFIRM_H
+#define SETUP_CONFIRM_H
+
+#include "proppage.h"
+
+class ConfirmPage:public PropertyPage
+{
+public:
+  ConfirmPage ();
+  virtual ~ConfirmPage () { };
+  bool Create ();
+  virtual void OnInit ();
+  virtual void OnActivate ();
+  virtual long OnNext ();
+  virtual long OnBack ();
+  virtual long OnUnattended ();
+private:
+  long whatNext ();
+};
+
+#endif /* SETUP_CONFIRM_H */
diff --git a/cygpackage.cc b/cygpackage.cc
deleted file mode 100644
index 32b9403..0000000
--- a/cygpackage.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2001, Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[email protected]>
- *
- */
-
-/* this is the parent class for all package operations. 
- */
-
-#include "cygpackage.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "io_stream.h"
-#include "compress.h"
-
-#include "package_version.h"
-#include "cygpackage.h"
-#include "LogSingleton.h"
-
-/* this constructor creates an invalid package - further details MUST be provided */
-cygpackage::cygpackage ():
-name (),
-vendor (),
-packagev (),
-canonical (),
-sdesc (),
-ldesc (),
-type (package_binary)
-{
-  /* FIXME: query the install database for the currently installed 
-   * version details
-   */
-}
-
-packageversion
-cygpackage::createInstance (const std::string& pkgname,
-                            const package_type_t type)
-{
-  cygpackage *temp = new cygpackage;
-  temp->name = pkgname;
-  temp->type = type;
-  return packageversion(temp);
-}
-
-packageversion
-cygpackage::createInstance (const std::string& pkgname,
-                            const std::string& version,
-			    package_type_t const newtype)
-{
-  cygpackage *temp = new cygpackage;
-  temp->name = pkgname;
-  temp->type = newtype;
-  temp->setCanonicalVersion (version);
-  return packageversion(temp);
-}
-
-/* tell the version */
-void
-cygpackage::setCanonicalVersion (const std::string& version)
-{
-  canonical = version;
-
-  const char *start = canonical.c_str();
-  const char *curr = strchr(start, '-');
-
-  if (curr)
-    {
-      const char *next;
-      while ((next = strchr (curr + 1, '-')))
-	curr = next;
-
-      /* package version appears after the last '-' in the version string */
-      packagev = curr + 1;
-      /* vendor version is everything up to that last '-' */
-      vendor.assign(canonical.c_str(), (size_t)(curr - start));
-    }
-  else
-    {
-      // FIXME: What's up with the "0"? It's probably a mistake, and should be
-      // "". It used to be written as 0, and was subject to a bizarre implicit
-      // conversion by the unwise String(int) constructor.
-      packagev = "0";
-      vendor = version;
-    }
-}
-
-cygpackage::~cygpackage ()
-{
-}
-
-const std::string
-cygpackage::Name ()
-{
-  return name;
-}
-
-const std::string
-cygpackage::Vendor_version ()
-{
-  return vendor;
-}
-
-const std::string
-cygpackage::Package_version ()
-{
-  return packagev;
-}
-
-std::string  const
-cygpackage::Canonical_version ()
-{
-  return canonical;
-}
-
-void
-cygpackage::set_sdesc (const std::string& desc)
-{
-  sdesc = desc;
-}
-
-void
-cygpackage::set_ldesc (const std::string& desc)
-{
-  ldesc = desc;
-}
-
-#if 0
-package_stability_t cygpackage::Stability ()
-{
-  return stability;
-}
-#endif
diff --git a/cygpackage.h b/cygpackage.h
deleted file mode 100644
index c6d0657..0000000
--- a/cygpackage.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2001, Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[email protected]>
- *
- */
-
-#ifndef SETUP_CYGPACKAGE_H
-#define SETUP_CYGPACKAGE_H
-
-/* This is a cygwin specific package class, that should be able to 
- * arbitrate acceess to cygwin binary packages amd cygwin source packages
- */
-
-#include "package_version.h"
-
-class io_stream;
-
-class cygpackage:public _packageversion
-{
-public:
-  virtual const std::string Name ();
-  virtual const std::string Vendor_version ();
-  virtual const std::string Package_version ();
-  virtual const std::string Canonical_version ();
-  virtual package_type_t Type ()
-  {
-    return type;
-  };
-  virtual void set_sdesc (const std::string& );
-  virtual void set_ldesc (const std::string& );
-  virtual const std::string SDesc ()
-  {
-    return sdesc;
-  };
-  virtual const std::string LDesc ()
-  {
-    return ldesc;
-  };
-
-  /* pass the name of the package when constructing */
-  void setCanonicalVersion (const std::string& );
-
-  virtual ~ cygpackage ();
-
-  /* pass the name of the package when constructing */
-  static packageversion createInstance (const std::string& pkgname,
-                                        const package_type_t type);
-
-  static packageversion createInstance (const std::string& pkgname,
-                                        const std::string& version,
-					package_type_t const);
-
-private:
-  cygpackage ();
-  std::string name;
-  std::string vendor;
-  std::string packagev;
-  std::string canonical;
-  std::string sdesc, ldesc;
-
-//  package_stability_t stability;
-  package_type_t type;
-};
-
-#endif /* SETUP_CYGPACKAGE_H */
diff --git a/desktop.cc b/desktop.cc
index 24908f8..927c02f 100644
--- a/desktop.cc
+++ b/desktop.cc
@@ -35,7 +35,6 @@
 #include "mklink2.h"
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
 #include "filemanip.h"
 #include "io_stream.h"
 #include "getopt++/BoolOption.h"
diff --git a/download.cc b/download.cc
index 129b226..0cb3352 100644
--- a/download.cc
+++ b/download.cc
@@ -39,21 +39,16 @@
 
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
 #include "package_source.h"
 
 #include "threebar.h"
 
 #include "Exception.h"
 
-#include "getopt++/BoolOption.h"
-
 using namespace std;
 
 extern ThreeBarProgressPage Progress;
 
-BoolOption IncludeSource (false, 'I', "include-source", "Automatically install source for every package installed");
-
 // Return true if selected checks pass, false if they don't and the
 // user chooses to delete the file; otherwise throw an exception.
 static bool
@@ -283,59 +278,42 @@ do_download_thread (HINSTANCE h, HWND owner)
   Progress.SetText3 ("");
 
   packagedb db;
-  /* calculate the amount needed */
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  const SolverTransactionList &t = db.solution.transactions();
+
+  /* calculate the total size of the download */
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
     {
-      packagemeta & pkg = *(i->second);
-      if (pkg.desired && (pkg.picked () || pkg.srcpicked ()))
-	{
-	  packageversion version = pkg.desired;
-	  packageversion sourceversion = version.sourcePackage();
-	  try 
-	    {
-	      if (pkg.picked())
-		{
-		    if (!check_for_cached (*version.source(), owner))
-		      total_download_bytes += version.source()->size;
-		}
-	      if (pkg.srcpicked () || IncludeSource)
-		{
-		    if (!check_for_cached (*sourceversion.source(), owner))
-		      total_download_bytes += sourceversion.source()->size;
-		}
-	    }
-	  catch (Exception * e)
-	    {
-	      // We know what to do with these..
-	      if (e->errNo() == APPERR_CORRUPT_PACKAGE)
-		fatal (owner, IDS_CORRUPT_PACKAGE, pkg.name.c_str());
-	      // Unexpected exception.
-	      throw e;
-	    }
-	}
+      if (i->type != SolverTransaction::transInstall)
+        continue;
+      packageversion version = i->version;
+
+      try
+        {
+          if (!check_for_cached (*version.source(), owner))
+            total_download_bytes += version.source()->size;
+        }
+      catch (Exception * e)
+        {
+          // We know what to do with these..
+          if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+            fatal (owner, IDS_CORRUPT_PACKAGE, version.Name().c_str());
+          // Unexpected exception.
+          throw e;
+        }
     }
 
   /* and do the download. FIXME: This here we assign a new name for the cached version
    * and check that above.
    */
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
     {
-      packagemeta & pkg = *(i->second);
-      if (pkg.desired && (pkg.picked () || pkg.srcpicked ()))
+      if (i->type != SolverTransaction::transInstall)
+        continue;
+      packageversion version = i->version;
+
 	{
 	  int e = 0;
-	  packageversion version = pkg.desired;
-	  packageversion sourceversion = version.sourcePackage();
-	  if (pkg.picked())
-	    {
-		e += download_one (*version.source(), owner);
-	    }
-	  if (sourceversion && (pkg.srcpicked() || IncludeSource))
-	    {
-		e += download_one (*sourceversion.source (), owner);
-	    }
+          e += download_one (*version.source(), owner);
 	  errors += e;
 	  if (e)
 	    download_failures.push_back (version);
diff --git a/filemanip.cc b/filemanip.cc
index d1d27be..265a2a7 100644
--- a/filemanip.cc
+++ b/filemanip.cc
@@ -43,22 +43,6 @@ get_file_size (const std::string& name)
   return rv;
 }
 
-std::string 
-base (const std::string& aString)
-{
-  if (!aString.size())
-    return "";
-  const char *s = aString.c_str();
-  std::string rv = s;
-  while (*s)
-    {
-      if ((*s == '/' || *s == ':' || *s == '\\') && s[1])
-	rv = s + 1;
-      s++;
-    }
-  return rv;
-}
-
 /* returns the number of characters of path that
  * precede the extension
  */
@@ -89,7 +73,7 @@ parse_filename (const string &fn, fileparse & f)
 
   f.tail = fn.substr (n, string::npos);
 
-  p = new_cstr_char_array (base (fn.substr (0, n)));
+  p = new_cstr_char_array (fn.substr (0, n));
   char const *ext;
   /* TODO: make const and non-const trail variant. */
   if ((ext = trail (p, "-src")))
diff --git a/filemanip.h b/filemanip.h
index 5594519..451211f 100644
--- a/filemanip.h
+++ b/filemanip.h
@@ -30,7 +30,6 @@ struct fileparse
 };
 
 int parse_filename (const std::string& fn, fileparse & f);
-std::string base (const std::string& );
 size_t get_file_size (const std::string& );
 std::string backslash (const std::string& s);
 const char * trail (const char *, const char *);
diff --git a/ini.cc b/ini.cc
index 87443a8..d807ed6 100644
--- a/ini.cc
+++ b/ini.cc
@@ -347,16 +347,16 @@ do_remote_ini (HWND owner)
 static bool
 do_ini_thread (HINSTANCE h, HWND owner)
 {
+  packagedb db;
+  db.init();
+
   bool ini_error = true;
+
   if (source == IDC_SOURCE_LOCALDIR)
     ini_error = do_local_ini (owner);
   else
     ini_error = do_remote_ini (owner);
 
-  packagedb db;
-  db.upgrade();
-  db.removeEmptyCategories();
-
   if (ini_error)
     return false;
 
diff --git a/inilex.ll b/inilex.ll
index 13422b1..95888cf 100644
--- a/inilex.ll
+++ b/inilex.ll
@@ -118,10 +118,13 @@ B64	[a-zA-Z0-9_-]
 "message:"		return MESSAGE;
 "Source:"		return SOURCEPACKAGE;
 "Build-Depends:"	return BUILDDEPENDS;
+"replace-versions:"	return REPLACE_VERSIONS;
 
 "category:"|"Section:"	return CATEGORY;
 "requires:"		return REQUIRES;
 [dD]"epends:"		return DEPENDS;
+[dD]"epends2:"		return DEPENDS;
+[oO]"bsoletes:"	return OBSOLETES;
 
 ^{STR}":"		ignore_line ();
 
diff --git a/iniparse.yy b/iniparse.yy
index 18ebe2a..e5c514b 100644
--- a/iniparse.yy
+++ b/iniparse.yy
@@ -37,6 +37,7 @@ extern int yylineno;
 
 %token STRING 
 %token SETUP_TIMESTAMP SETUP_VERSION PACKAGEVERSION INSTALL SOURCE SDESC LDESC
+%token REPLACE_VERSIONS
 %token CATEGORY DEPENDS REQUIRES
 %token T_PREV T_CURR T_TEST T_OTHER
 %token MD5 SHA512
@@ -45,6 +46,7 @@ extern int yylineno;
 %token COMMA NL AT
 %token OPENBRACE CLOSEBRACE EQUAL GT LT GTEQUAL LTEQUAL 
 %token BUILDDEPENDS
+%token OBSOLETES
 %token MESSAGE
 %token ARCH RELEASE
 
@@ -103,6 +105,9 @@ singleitem /* non-empty */
  | DEPENDS { iniBuilder->buildBeginDepends(); } versionedpackagelist NL
  | REQUIRES { iniBuilder->buildBeginDepends(); } versionedpackagelistsp NL
  | BUILDDEPENDS { iniBuilder->buildBeginBuildDepends(); } versionedpackagelist NL
+ | OBSOLETES { iniBuilder->buildBeginObsoletes(); } versionedpackagelist NL
+ | REPLACE_VERSIONS versionlist NL
+
  | MESSAGE STRING STRING NL	{ iniBuilder->buildMessage ($2, $3); }
  | error NL			{ yyerror (std::string("unrecognized line ")
 					  + stringify(yylineno)
@@ -153,4 +158,8 @@ operator /* non-empty */
  | GTEQUAL { iniBuilder->buildPackageListOperator (PackageSpecification::MoreThanEquals); }
  ;
 
+versionlist: /* empty */
+ | versionlist STRING { iniBuilder->buildPackageReplaceVersionsList ($2); }
+ ;
+
 %%
diff --git a/install.cc b/install.cc
index 366aeb2..3dd491e 100644
--- a/install.cc
+++ b/install.cc
@@ -68,7 +68,6 @@ static long long int total_bytes = 0;
 static long long int total_bytes_sofar = 0;
 static int package_bytes = 0;
 
-extern BoolOption IncludeSource;
 static BoolOption NoReplaceOnReboot (false, 'r', "no-replaceonreboot",
 				     "Disable replacing in-use files on next "
 				     "reboot.");
@@ -815,26 +814,21 @@ do_install_thread (HINSTANCE h, HWND owner)
   /* Writes Cygwin/setup/rootdir registry value */
   create_install_root ();
 
-  vector <packagemeta *> install_q, uninstall_q, sourceinstall_q;
+  vector <packageversion> install_q, uninstall_q, sourceinstall_q;
 
   packagedb db;
+  const SolverTransactionList &t = db.solution.transactions();
 
   /* Calculate the amount of data to md5sum */
   Progress.SetText1("Calculating...");
   long long int md5sum_total_bytes = 0;
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
   {
-    packagemeta & pkg = *(i->second);
+    packageversion version = i->version;
 
-    if (pkg.picked())
+    if (i->type == SolverTransaction::transInstall)
     {
-      md5sum_total_bytes += pkg.desired.source()->size;
-    }
-
-    if (pkg.srcpicked() || IncludeSource)
-    {
-      md5sum_total_bytes += pkg.desired.sourcePackage ().source()->size;
+      md5sum_total_bytes += version.source()->size;
     }
   }
 
@@ -844,58 +838,37 @@ do_install_thread (HINSTANCE h, HWND owner)
      net install, the hashes will have already been verified at download
      time, and all calls to check_hash() below should instantly return.  */
   long long int md5sum_total_bytes_sofar = 0;
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
   {
-    packagemeta & pkg = *(i->second);
+    packageversion version = i->version;
 
-    if (pkg.picked())
+    if (i->type == SolverTransaction::transInstall)
     {
       try
       {
-        (*pkg.desired.source ()).check_hash ();
+        (*version.source ()).check_hash ();
       }
       catch (Exception *e)
       {
-        if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES)
-          pkg.pick (false);
+        yesno (owner, IDS_SKIP_PACKAGE, e->what());
       }
-      if (pkg.picked())
       {
-        md5sum_total_bytes_sofar += pkg.desired.source()->size;
-        total_bytes += pkg.desired.source()->size;
-        install_q.push_back (&pkg);
+        md5sum_total_bytes_sofar += version.source()->size;
+        total_bytes += version.source()->size;
+
+        // source packages are kept in a separate queue as they are installed
+        // differently: root is /usr/src, install isn't recorded, etc.
+        if (version.Type() == package_source)
+          sourceinstall_q.push_back (version);
+        else
+          install_q.push_back (version);
       }
     }
 
-    if (pkg.srcpicked() || IncludeSource)
+    /* Uninstall, upgrade or reinstall */
+    if (i->type == SolverTransaction::transErase)
     {
-      bool skiprequested = false ;
-      try
-      {
-        (*pkg.desired.sourcePackage ().source ()).check_hash ();
-      }
-      catch (Exception *e)
-      {
-        if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES)
-	{
-	  skiprequested = true ; //(err occurred,) skip pkg desired
-          pkg.srcpick (false);
-	}
-      }
-      if (pkg.srcpicked() || (IncludeSource && !skiprequested))
-      {
-        md5sum_total_bytes_sofar += pkg.desired.sourcePackage ().source()->size;
-        total_bytes += pkg.desired.sourcePackage ().source()->size;
-        sourceinstall_q.push_back (&pkg);
-      }
-    }
-
-    /* Upgrade or reinstall */
-    if ((pkg.installed && pkg.desired != pkg.installed)
-        || pkg.picked ())
-    {
-      uninstall_q.push_back (&pkg);
+      uninstall_q.push_back (version);
     }
 
     if (md5sum_total_bytes > 0)
@@ -904,27 +877,31 @@ do_install_thread (HINSTANCE h, HWND owner)
 
   /* start with uninstalls - remove files that new packages may replace */
   Progress.SetBar2(0);
-  for (vector <packagemeta *>::iterator i = uninstall_q.begin ();
+  for (vector <packageversion>::iterator i = uninstall_q.begin ();
        i != uninstall_q.end (); ++i)
   {
-    myInstaller.preremoveOne (**i);
+    packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
+    myInstaller.preremoveOne (*pkgm);
     Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size());
   }
 
   Progress.SetBar2(0);
-  for (vector <packagemeta *>::iterator i = uninstall_q.begin ();
+  for (vector <packageversion>::iterator i = uninstall_q.begin ();
        i != uninstall_q.end (); ++i)
   {
-    myInstaller.uninstallOne (**i);
+    packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
+    myInstaller.uninstallOne (*pkgm);
     Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size());
   }
 
-  for (vector <packagemeta *>::iterator i = install_q.begin ();
+  for (vector <packageversion>::iterator i = install_q.begin ();
        i != install_q.end (); ++i)
   {
-    packagemeta & pkg = **i;
+    packageversion & pkg = *i;
+    packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
+
     try {
-      myInstaller.installOne (pkg, pkg.desired, *pkg.desired.source(),
+      myInstaller.installOne (*pkgm, pkg, *pkg.source(),
                               "cygfile://", "/", owner);
     }
     catch (exception *e)
@@ -939,12 +916,12 @@ do_install_thread (HINSTANCE h, HWND owner)
     }
   }
 
-  for (vector <packagemeta *>::iterator i = sourceinstall_q.begin ();
+  for (vector <packageversion>::iterator i = sourceinstall_q.begin ();
        i != sourceinstall_q.end (); ++i)
   {
-    packagemeta & pkg = **i;
-    myInstaller.installOne (pkg, pkg.desired.sourcePackage(),
-                            *pkg.desired.sourcePackage().source(),
+    packagemeta *pkgm = db.findSource (PackageSpecification(i->Name()));
+    packageversion & pkg = *i;
+    myInstaller.installOne (*pkgm, pkg, *pkg.source(),
                             "cygfile://", "/usr/src/", owner);
   }
 
diff --git a/libsolv.cc b/libsolv.cc
new file mode 100644
index 0000000..135d9af
--- /dev/null
+++ b/libsolv.cc
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (c) 2017 Jon Turney
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#include "libsolv.h"
+#include "package_db.h"
+#include "package_meta.h"
+
+#include "solv/solver.h"
+#include "solv/solverdebug.h"
+#include "solv/evr.h"
+
+#include "LogSingleton.h"
+#include  <iomanip>
+
+// ---------------------------------------------------------------------------
+// Utility functions for mapping between Operators and Relation Ids
+// ---------------------------------------------------------------------------
+
+static Id
+Operator2RelId(PackageSpecification::_operators op)
+{
+  switch (op)
+    {
+    case PackageSpecification::Equals:
+      return REL_EQ;
+    case PackageSpecification::LessThan:
+      return REL_LT;
+    case PackageSpecification::MoreThan:
+      return REL_GT;
+    case PackageSpecification::LessThanEquals:
+      return REL_LT | REL_EQ;
+    case PackageSpecification::MoreThanEquals:
+      return REL_GT | REL_EQ;
+    }
+
+  return 0;
+}
+
+static PackageSpecification::_operators
+RelId2Operator(Id id)
+{
+  switch (id)
+    {
+    case REL_EQ:
+      return PackageSpecification::Equals;
+    case REL_LT:
+      return PackageSpecification::LessThan;
+    case REL_GT:
+      return PackageSpecification::MoreThan;
+    case REL_LT | REL_EQ:
+      return PackageSpecification::LessThanEquals;
+    case REL_GT | REL_EQ:
+      return PackageSpecification::MoreThanEquals;
+    }
+
+  return PackageSpecification::Equals;
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolvableVersion
+//
+// a wrapper around a libsolv Solvable
+// ---------------------------------------------------------------------------
+
+Id
+SolvableVersion::name_id () const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+  return solvable->name;
+}
+
+const std::string
+SolvableVersion::Name () const
+{
+  if (!id)
+    return "";
+  return pool_id2str(pool, name_id());
+}
+
+const std::string
+SolvableVersion::Canonical_version() const
+{
+  if (!id)
+    return "";
+  Solvable *solvable = pool_id2solvable(pool, id);
+  return std::string(pool_id2str(pool, solvable->evr));
+}
+
+package_type_t
+SolvableVersion::Type () const
+{
+  if (!id)
+    return package_binary;
+  Solvable *solvable = pool_id2solvable(pool, id);
+  if (solvable->arch == ARCH_SRC)
+    return package_source;
+  else
+    return package_binary;
+}
+
+const PackageDepends
+SolvableVersion::depends() const
+{
+  return deplist(SOLVABLE_REQUIRES);
+}
+
+const PackageDepends
+SolvableVersion::obsoletes() const
+{
+  return deplist(SOLVABLE_OBSOLETES);
+}
+
+// helper function which returns the deplist for a given key, as a PackageDepends
+const PackageDepends
+SolvableVersion::deplist(Id keyname) const
+{
+  static PackageDepends empty_package;
+  if (!id)
+    return empty_package;
+  Solvable *solvable = pool_id2solvable(pool, id);
+
+  Queue q;
+  queue_init(&q);
+
+  if (repo_lookup_idarray(solvable->repo, id, keyname, &q))
+    {
+      // convert
+      PackageDepends dep;
+
+      for (int i = 0; i < q.count; i++)
+        {
+#ifdef DEBUG
+          Log (LOG_PLAIN) << "dep " << std::hex << q.elements[i] << ": " << pool_dep2str(pool, q.elements[i]) << endLog;
+#endif
+
+          const char *name = pool_id2str(pool, q.elements[i]);
+          PackageSpecification *spec = new PackageSpecification (name);
+
+          if (ISRELDEP(id))
+            {
+              Reldep *rd = GETRELDEP(pool, id);
+              spec->setOperator(RelId2Operator(rd->flags));
+              spec->setVersion(pool_id2str(pool, rd->evr));
+            }
+
+          dep.push_back (spec);
+        }
+
+      queue_empty(&q);
+
+      return dep;
+    }
+
+  // otherwise, return an empty depends list
+  return empty_package;
+}
+
+const std::string
+SolvableVersion::SDesc () const
+{
+  if (!id)
+    return "";
+  Solvable *solvable = pool_id2solvable(pool, id);
+  const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY);
+
+  if (!sdesc)
+    return "";
+
+  return sdesc;
+}
+
+const std::string
+SolvableVersion::sourcePackageName () const
+{
+  if (!id)
+    return "";
+
+  // extract source package name
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id spkg = repo_lookup_id(solvable->repo, id, SOLVABLE_SOURCENAME);
+
+  // has no such attribute
+  if (!spkg)
+    return "";
+
+  return std::string(pool_id2str(pool, spkg));
+}
+
+SolvableVersion
+SolvableVersion::sourcePackage () const
+{
+  if (!id)
+    return SolvableVersion();
+
+  // extract source package id
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
+  Id spkg_id = repo_lookup_id(solvable->repo, id, spkg_attr);
+
+  // has no such attribute
+  if (!spkg_id)
+    return SolvableVersion();
+
+  return SolvableVersion(spkg_id, pool);
+}
+
+void
+SolvableVersion::fixup_spkg_id (SolvableVersion spkg_id) const
+{
+  if (!id)
+    return;
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Repodata *data = repo_last_repodata(solvable->repo);
+  Id handle = id;
+  Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
+  repodata_set_id(data, handle, spkg_attr, spkg_id.id);
+}
+
+packagesource *
+SolvableVersion::source() const
+{
+  static packagesource empty_source = packagesource();
+  if (!id) {
+    return &empty_source;
+  }
+
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1);
+  return (packagesource *)repo_lookup_num(solvable->repo, id, psrc_attr, (unsigned long long)&empty_source);
+}
+
+bool
+SolvableVersion::accessible () const
+{
+  // The 'accessible' check used to test if an archive was available locally or
+  // from a mirror.
+  //
+  // This seems utterly pointless. as binary packages which aren't 'accessible'
+  // never get to appear in the package list.
+  //
+  // For source packages, it's equivalent to the bool conversion operator.)
+  //
+  if (id != 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+package_stability_t
+SolvableVersion::Stability () const
+{
+  if (!id)
+    return TRUST_UNKNOWN;
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id stability_attr = pool_str2id(pool, "solvable:stability", 1);
+  return (package_stability_t)repo_lookup_num(solvable->repo, id, stability_attr, TRUST_UNKNOWN);
+}
+
+bool
+SolvableVersion::operator <(SolvableVersion const &rhs) const
+{
+  return (compareVersions(*this, rhs) < 0);
+}
+
+bool
+SolvableVersion::operator ==(SolvableVersion const &rhs) const
+{
+  return (compareVersions(*this, rhs) == 0);
+}
+
+bool
+SolvableVersion::operator !=(SolvableVersion const &rhs) const
+{
+  return (compareVersions(*this, rhs) != 0);
+}
+
+int
+SolvableVersion::compareVersions(const SolvableVersion &a,
+                                 const SolvableVersion &b)
+{
+  if (a.id == b.id)
+    return 0;
+
+  // if a and b are different, at least one of them has a pool
+  Pool *pool = a.pool ? a.pool : b.pool;
+
+  Solvable *sa = a.id ? pool_id2solvable(a.pool, a.id) : NULL;
+  Solvable *sb = b.id ? pool_id2solvable(b.pool, b.id) : NULL;
+
+  // empty versions compare as if their version is the empty string
+  Id evra = sa ? sa->evr : pool_str2id(pool, "", 1);
+  Id evrb = sb ? sb->evr : pool_str2id(pool, "", 1);
+
+  return pool_evrcmp(pool, evra, evrb, EVRCMP_COMPARE);
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolverPool
+//
+// a simplified wrapper for libsolv
+// ---------------------------------------------------------------------------
+
+static
+void debug_callback(Pool *pool, void *data, int type, const char *str)
+{
+  if (type & (SOLV_FATAL|SOLV_ERROR))
+    LogPlainPrintf("libsolv: %s", str);
+  else
+    LogBabblePrintf("libsolv: %s", str);
+}
+
+SolverPool::SolverPool()
+{
+  init();
+}
+
+void
+SolverPool::init()
+{
+  /* create a pool */
+  pool = pool_create();
+
+  pool_setdebugcallback(pool, debug_callback, NULL);
+
+  int level = 1;
+#if DEBUG
+  level = 3;
+#endif
+  pool_setdebuglevel(pool, level);
+
+  /* create the repo to hold installed packages */
+  SolvRepo *installed = getRepo("_installed");
+  pool_set_installed(pool, installed->repo);
+}
+
+void
+SolverPool::clear()
+{
+  repos.clear();
+  pool_free(pool);
+  pool = NULL;
+
+  init();
+}
+
+SolvRepo *
+SolverPool::getRepo(const std::string &name, bool test)
+{
+  RepoList::iterator i = repos.find(name);
+  if (i != repos.end())
+    return i->second;
+
+  /* create repo if not found */
+  SolvRepo *r = new(SolvRepo);
+  r->repo = repo_create(pool, name.c_str());
+
+  /* create attribute store, with no local pool */
+  r->data = repo_add_repodata(r->repo, 0);
+
+  /* remember if this is a test stability repo */
+  r->test = test;
+
+  /* set default priority */
+  r->repo->priority = SolvRepo::priorityNormal;
+
+  repos[name] = r;
+
+  return r;
+}
+
+/*
+  Helper function to convert a PackageDepends list to libsolv dependencies.
+*/
+Id
+SolverPool::makedeps(Repo *repo, PackageDepends *requires)
+{
+  Id deps = 0;
+
+  for (PackageDepends::iterator i = requires->begin();
+       i != requires->end();
+       i++)
+    {
+      Id name = pool_str2id(pool, (*i)->packageName().c_str(), 1);
+
+      if ((*i)->version().size() == 0)
+        {
+          // no relation, so dependency is just on package name
+          deps = repo_addid_dep(repo, deps, name, 0);
+        }
+      else
+        {
+          // otherwise, dependency is on package name with a version condition
+          Id evr = pool_str2id(pool, (*i)->version().c_str(), 1);
+          int rel = pool_rel2id(pool, name, evr, Operator2RelId((*i)->op()), 1);
+
+          deps = repo_addid_dep(repo, deps, rel, 0);
+        }
+    }
+
+  return deps;
+}
+
+SolvableVersion
+SolverPool::addPackage(const std::string& pkgname, const addPackageData &pkgdata)
+{
+  std::string repoName = pkgdata.reponame;
+  bool test = false;
+
+  /* It's simplest to place test packages into a separate repo, and
+     then arrange for that repo to have low priority, if we don't want
+     to install those packages by default */
+
+  if (pkgdata.stability == TRUST_TEST && repoName != "_installed")
+    {
+      repoName = pkgdata.reponame + "_test_";
+      test = true;
+    }
+
+  SolvRepo *r = getRepo(repoName, test);
+  Repo *repo = r->repo;
+
+  /* create a solvable */
+  Id s = repo_add_solvable(repo);
+  Solvable *solvable = pool_id2solvable(pool, s);
+
+  /* initialize solvable for this packageo/version/etc.  */
+  solvable->name = pool_str2id(pool, pkgname.c_str(), 1);
+  solvable->arch = (pkgdata.type == package_binary) ? ARCH_ANY : ARCH_SRC;
+  solvable->evr = pool_str2id(repo->pool, pkgdata.version.c_str(), 1);
+  solvable->vendor = pool_str2id(repo->pool, pkgdata.vendor.c_str(), 1);
+  solvable->provides = repo_addid_dep(repo, solvable->provides, pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);
+  if (pkgdata.requires)
+    solvable->requires = makedeps(repo, pkgdata.requires);
+  if (pkgdata.obsoletes)
+    solvable->obsoletes = makedeps(repo, pkgdata.obsoletes);
+
+  /* a solvable can also store arbitrary attributes not needed for dependency
+     resolution, if we need them */
+
+  Repodata *data = r->data;
+  Id handle = s;
+#if DEBUG
+  Log (LOG_PLAIN) << "solvable " << s << " name " << pkgname << endLog;
+#endif
+
+  /* store short description attribute */
+  repodata_set_str(data, handle, SOLVABLE_SUMMARY, pkgdata.sdesc.c_str());
+  /* store long description attribute */
+  repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, pkgdata.ldesc.c_str());
+
+  /* store source-package attribute */
+  const std::string sname = pkgdata.spkg.packageName();
+  if (!sname.empty())
+    repodata_set_id(data, handle, SOLVABLE_SOURCENAME, pool_str2id(pool, sname.c_str(), 1));
+  else
+    repodata_set_void(data, handle, SOLVABLE_SOURCENAME);
+  /* solvable:sourceevr may also be available from spkg but assumed to be same
+     as evr for the moment */
+
+  /* store source-package id */
+  /* (If the source-package hasn't been seen yet, we don't know what it's Id
+     will be.  That gets fixed up later.) */
+  if (pkgdata.spkg_id)
+    {
+      Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
+      repodata_set_id(data, handle, spkg_attr, pkgdata.spkg_id.id);
+    }
+
+  /* we could store packagesource information as attributes ...
+
+     e.g.
+       size     SOLVABLE_DOWNLOADSIZE
+       pathname SOLVABLE_MEDIAFILE
+       site     SOLVABLE_MEDIABASE
+       checksum SOLVABLE_CHECKSUM
+
+     ... but for the moment, we just store a pointer to a packagesource object
+  */
+  Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1);
+  packagesource *psrc = new packagesource(pkgdata.archive);
+  repodata_set_num(data, handle, psrc_attr, (intptr_t)psrc);
+
+  /* store stability level attribute */
+  Id stability_attr = pool_str2id(pool, "solvable:stability", 1);
+  repodata_set_num(data, handle, stability_attr, pkgdata.stability);
+
+#if 0
+  repodata_internalize(data);
+
+  /* debug: verify the attributes we've just set get retrieved correctly */
+  SolvableVersion sv = SolvableVersion(s, pool);
+  const std::string check_sdesc = sv.SDesc();
+  if (pkgdata.sdesc.compare(check_sdesc) != 0) {
+    Log (LOG_PLAIN) << pkgname << " has sdesc mismatch: '" << pkgdata.sdesc << "' and '"
+                    << check_sdesc << "'" << endLog;
+  }
+  if (!sname.empty()) {
+    SolvableVersion check_spkg = sv.sourcePackage();
+    Solvable *check_spkg_solvable = pool_id2solvable(pool, check_spkg.id);
+    std::string check_sname = pool_id2str(pool, check_spkg_solvable->name);
+    if (sname.compare(check_sname) != 0) {
+      Log (LOG_PLAIN) << pkgname << " has spkg mismatch: '" << pkgdata.spkg.packageName()
+                      << "' and '" << check_sname << "'" << endLog;
+    }
+  }
+  packagesource *check_archive = sv.source();
+  if (check_archive != psrc)
+    Log (LOG_PLAIN) << pkgname << " has archive mismatch: " << psrc
+                    << " and " << check_archive << endLog;
+  package_stability_t check_stability = sv.Stability();
+  if (check_stability != pkgdata.stability) {
+    Log (LOG_PLAIN) << pkgname << " has stability mismatch: " << pkgdata.stability
+                    << " and " << check_stability << endLog;
+  }
+#endif
+
+  return SolvableVersion(s, pool);
+}
+
+void
+SolverPool::internalize()
+{
+  /* Make attribute data available to queries */
+  for (RepoList::iterator i = repos.begin();
+       i != repos.end();
+       i++)
+    {
+      repodata_internalize(i->second->data);
+    }
+}
+
+void
+SolverTasks::setTasks()
+{
+  // go through all packages, adding changed ones to the solver task list
+  packagedb db;
+  tasks.clear();
+
+  for (packagedb::packagecollection::iterator p = db.packages.begin ();
+       p != db.packages.end (); ++p)
+    {
+      packagemeta *pkg = p->second;
+
+      // decode UI state to action
+      // keep and skip need attention only when they differ from the
+      // solver's solution
+      if (pkg->installed != pkg->desired)
+        {
+          if (pkg->desired)
+            add(pkg->desired, taskInstall); // install/upgrade
+          else
+            add(pkg->installed, taskUninstall); // uninstall
+        }
+      else if (pkg->installed)
+        {
+          if (pkg->picked())
+            add(pkg->installed, taskReinstall); // reinstall
+          else if (pkg->installed != pkg->default_version)
+            add(pkg->installed, taskKeep); // keep
+          else
+            {
+              // if installed (with no action selected), but blacklisted, force
+              // a distupgrade of this package
+              if (pkg->isBlacklisted(pkg->installed))
+                {
+                  add(pkg->installed, taskForceDistUpgrade);
+                }
+            }
+        }
+      else if (pkg->default_version)
+        add(pkg->default_version, taskSkip); // skip
+
+      // only install action makes sense for source packages
+      if (pkg->srcpicked())
+        {
+          if (pkg->desired)
+            add(pkg->desired.sourcePackage(), taskInstall);
+          else
+            add(pkg->installed.sourcePackage(), taskInstall);
+        }
+    }
+}
+
+void
+SolverPool::use_test_packages(bool use_test_packages)
+{
+  // Give repos containing test packages higher priority than normal
+  // if wanted, otherwise lower priority.
+  SolvRepo::priority_t p = use_test_packages ? SolvRepo::priorityNormal : SolvRepo::priorityLow;
+  for (RepoList::iterator i = repos.begin();
+       i != repos.end();
+       i++)
+    {
+      if (i->second->test)
+        i->second->setPriority(p);
+    }
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolverSolution
+//
+// A wrapper around the libsolv solver
+// ---------------------------------------------------------------------------
+
+SolverSolution::SolverSolution(SolverPool &_pool) : pool(_pool), solv(NULL)
+{
+  queue_init(&job);
+}
+
+SolverSolution::~SolverSolution()
+{
+  clear();
+}
+
+void
+SolverSolution::clear()
+{
+  if (solv)
+    {
+      solver_free(solv);
+      solv = NULL;
+    }
+  queue_free(&job);
+}
+
+void
+SolverSolution::trans2db() const
+{
+  packagedb db;
+  // First reset all packages to the "no change" state
+  for (packagedb::packagecollection::iterator i = db.packages.begin();
+       i != db.packages.end(); i++)
+    {
+      packagemeta *pkg = i->second;
+      pkg->desired = pkg->default_version = pkg->installed;
+      pkg->pick(false);
+    }
+  // Now make changes according to trans.  transErase requires some
+  // care; it could either be a "plain" uninstall, or it could be
+  // paired with a transInstall for an upgrade/downgrade or reinstall.
+  for (SolverTransactionList::const_iterator i = trans.begin();
+       i != trans.end(); i++)
+    {
+      const packageversion & pv = i->version;
+      packagemeta *pkg = db.findBinary(PackageSpecification(pv.Name()));
+      if (!pkg)
+        // Can't happen - throw an exception?
+        {
+          Log (LOG_PLAIN) << "Can't happen.  No packagemeta for "
+                          << pv.Name() << endLog;
+          return;
+        }
+      switch (i->type)
+        {
+        case SolverTransaction::transInstall:
+          if (pv.Type() == package_binary)
+            {
+              pkg->desired = pkg->default_version = pv;
+              pkg->pick(true);
+            }
+          else // source package
+            pkg->srcpick(true);
+          break;
+        case SolverTransaction::transErase:
+          // Only relevant if pkg is still in its "no change" state
+          if (pkg->desired == pkg->installed && !pkg->picked())
+            pkg->desired = pkg->default_version = packageversion();
+          break;
+        default:
+          break;
+        }
+    }
+}
+
+void
+SolverSolution::db2trans()
+{
+  trans.clear();
+  packagedb db;
+
+  for (packagedb::packagecollection::iterator p = db.packages.begin ();
+       p != db.packages.end (); ++p)
+    {
+      packagemeta *pkg = p->second;
+      if (pkg->desired && pkg->picked()) // install/upgrade/reinstall
+        {
+          trans.push_back(SolverTransaction(pkg->desired, SolverTransaction::transInstall));
+          if (pkg->installed)
+            trans.push_back(SolverTransaction(pkg->installed, SolverTransaction::transErase));
+        }
+      else if (!pkg->desired && pkg->installed) // uninstall
+        trans.push_back(SolverTransaction(pkg->installed, SolverTransaction::transErase));
+
+      if (pkg->srcpicked())
+        {
+          if (pkg->desired)
+            trans.push_back(SolverTransaction(pkg->desired.sourcePackage(), SolverTransaction::transInstall));
+          else
+            trans.push_back(SolverTransaction(pkg->installed.sourcePackage(), SolverTransaction::transInstall));
+        }
+    }
+}
+
+static
+std::ostream &operator<<(std::ostream &stream,
+                         SolverTransaction::transType type)
+{
+  switch (type)
+    {
+    case SolverTransaction::transInstall:
+      stream << "install";
+      break;
+    case SolverTransaction::transErase:
+      stream << "erase";
+      break;
+    default:
+      stream << "unknown";
+    }
+  return stream;
+}
+
+void
+SolverSolution::tasksToJobs(SolverTasks &tasks, updateMode update, Queue &job)
+{
+  // solver accepts a queue containing pairs of (cmd, id) tasks
+  // cmd is job and selection flags ORed together
+  for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
+       i != tasks.tasks.end();
+       i++)
+    {
+      const SolvableVersion &sv = (*i).first;
+
+      switch ((*i).second)
+        {
+        case SolverTasks::taskInstall:
+          queue_push2(&job, SOLVER_INSTALL | SOLVER_SOLVABLE, sv.id);
+          break;
+        case SolverTasks::taskUninstall:
+          queue_push2(&job, SOLVER_ERASE | SOLVER_SOLVABLE, sv.id);
+          break;
+        case SolverTasks::taskReinstall:
+          // we don't know how to ask solver for this, so we just add the erase
+          // and install later
+          break;
+        case SolverTasks::taskKeep:
+          queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE, sv.id);
+          break;
+        case SolverTasks::taskSkip:
+          queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE_NAME, sv.name_id());
+          break;
+        case SolverTasks::taskForceDistUpgrade:
+          queue_push2(&job, SOLVER_DISTUPGRADE | SOLVER_SOLVABLE, sv.id);
+          break;
+        default:
+          Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog;
+        }
+    }
+
+  // Ask solver to update packages
+  switch (update)
+    {
+    case keep:
+      break;
+
+    case updateBest:
+      // Update to best version
+      queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0);
+      break;
+
+    case updateForce:
+      // Bring installed, non-orphaned packages in sync with the ones in the
+      // repository
+      queue_push2(&job, SOLVER_DISTUPGRADE | SOLVER_SOLVABLE_ALL, 0);
+      break;
+    }
+
+  // Ask solver to check dependencies of installed packages.
+  queue_push2(&job, SOLVER_VERIFY | SOLVER_SOLVABLE_ALL, 0);
+}
+
+bool
+SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_packages)
+{
+  Log (LOG_PLAIN) << "solving: " << tasks.tasks.size() << " tasks," <<
+    " update: " << (update ? "yes" : "no") << "," <<
+    " use test packages: " << (use_test_packages ? "yes" : "no") << endLog;
+
+  pool.use_test_packages(use_test_packages);
+
+  queue_free(&job);
+  tasksToJobs(tasks, update, job);
+
+  if (!solv)
+    solv = solver_create(pool.pool);
+
+  return solve();
+}
+
+bool
+SolverSolution::solve()
+{
+  solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
+  solver_set_flag(solv, SOLVER_FLAG_ALLOW_DOWNGRADE, 0);
+  solver_set_flag(solv, SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE, 1);
+  solver_set_flag(solv, SOLVER_FLAG_DUP_ALLOW_DOWNGRADE, 1);
+  solver_solve(solv, &job);
+
+  int pcnt = solver_problem_count(solv);
+  solver_printdecisions(solv);
+
+  solutionToTransactionList();
+
+  return (pcnt == 0);
+}
+
+void
+SolverSolution::solutionToTransactionList()
+{
+  // get transactions for solution
+  Transaction *t = solver_create_transaction(solv);
+  transaction_order(t, 0);
+  transaction_print(t);
+
+  // massage into SolverTransactions form
+  trans.clear();
+  for (int i = 0; i < t->steps.count; i++)
+    {
+      Id id = t->steps.elements[i];
+      SolverTransaction::transType tt = type(t, i);
+      if (tt != SolverTransaction::transIgnore)
+        trans.push_back(SolverTransaction(SolvableVersion(id, pool.pool), tt));
+    }
+
+  transaction_free(t);
+
+  dumpTransactionList();
+}
+
+void
+SolverSolution::augmentTasks(SolverTasks &tasks)
+{
+  // add install and remove tasks for anything marked as reinstall
+  for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
+       i != tasks.tasks.end();
+       i++)
+    {
+      const SolvableVersion &sv = (*i).first;
+
+      if (((*i).second) == SolverTasks::taskReinstall)
+        {
+          trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
+                                            SolverTransaction::transErase));
+          trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
+                                            SolverTransaction::transInstall));
+        }
+    }
+}
+
+void
+SolverSolution::addSource(bool include_source)
+{
+  // if include_source mode is on, also install source for everything we are
+  // installing
+  if (include_source)
+    {
+      // (this uses indicies into the vector, as iterators might become
+      // invalidated by doing push_back)
+      size_t n = trans.size();
+      for (size_t i = 0; i < n; i++)
+        {
+          if (trans[i].type == SolverTransaction::transInstall)
+            {
+              SolvableVersion src_version = trans[i].version.sourcePackage();
+              if (src_version)
+                trans.push_back(SolverTransaction(src_version,
+                                                  SolverTransaction::transInstall));
+            }
+        }
+    }
+}
+
+void
+SolverSolution::dumpTransactionList() const
+{
+  if (trans.size())
+    {
+      Log (LOG_PLAIN) << "Augmented Transaction List:" << endLog;
+      for (SolverTransactionList::const_iterator i = trans.begin ();
+           i != trans.end ();
+           ++i)
+        {
+          Log (LOG_PLAIN) << std::setw(4) << std::distance(trans.begin(), i)
+                          << std::setw(8) << i->type
+                          << std::setw(48) << i->version.Name()
+                          << std::setw(20) << i->version.Canonical_version() << endLog;
+        }
+    }
+  else
+    Log (LOG_PLAIN) << "Augmented Transaction List: is empty" << endLog;
+}
+
+void SolverSolution::applyDefaultProblemSolutions()
+{
+  // adjust the task list with the default solutions
+  int pcnt = solver_problem_count(solv);
+  for (Id problem = 1; problem <= pcnt; problem++)
+    {
+      int scnt = solver_solution_count(solv, problem);
+      solver_take_solution(solv, problem, scnt, &job);
+    }
+
+  // re-solve
+  if (!solve())
+    Log (LOG_PLAIN) << "default solutions did not solve all problems!" << endLog;
+}
+
+const SolverTransactionList &
+SolverSolution::transactions() const
+{
+  return trans;
+}
+
+// Construct a string reporting the problems and solutions
+std::string
+SolverSolution::report() const
+{
+  packagedb db;
+  std::string r = "";
+  int pcnt = solver_problem_count(solv);
+  for (Id problem = 1; problem <= pcnt; problem++)
+    {
+      r += "Problem " + std::to_string(problem) + "/" + std::to_string(pcnt);
+      r += "\n";
+
+      Id probr = solver_findproblemrule(solv, problem);
+      Id dep, source, target;
+      SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
+      if (source == db.basepkg.id)
+        r += "package " + std::string(pool_dep2str(pool.pool, dep)) + " is a Base package and is therefore required";
+      else
+        r += solver_problemruleinfo2str(solv, type, source, target, dep);
+      r += "\n";
+
+      int scnt = solver_solution_count(solv, problem);
+      for (Id solution = 1; solution <= scnt; solution++)
+        {
+          r += "Solution " + std::to_string(solution) + "/" + std::to_string(scnt);
+          if (solution == scnt) r += " (default)";
+          r += "\n";
+
+          Id p, rp, element;
+          element = 0;
+          while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
+            {
+              r += "  - ";
+              if (p == db.basepkg.id)
+                r += "allow deinstallation of Base packages";
+              else
+                r += solver_solutionelement2str(solv, p, rp);
+              r += "\n";
+            }
+        }
+    }
+
+  // since package arch isn't set usefully at the moment, remove all ".any" from
+  // package names in the problem report string.
+  std::string any = ".any";
+  size_t pos;
+  while ((pos = r.find(any)) != std::string::npos)
+    {
+      r.replace(pos, any.length(), "");
+    }
+
+  return r;
+}
+
+// helper function to map transaction type
+SolverTransaction::transType
+SolverSolution::type(Transaction *trans, int pos)
+{
+  Id tt = transaction_type(trans, trans->steps.elements[pos],
+                           SOLVER_TRANSACTION_SHOW_ACTIVE);
+
+  // if active side of transaction is nothing, ask again for passive side of
+  // transaction
+  if (tt == SOLVER_TRANSACTION_IGNORE)
+    tt = transaction_type(trans, trans->steps.elements[pos], 0);
+
+  switch(tt)
+    {
+    case SOLVER_TRANSACTION_INSTALL:
+    case SOLVER_TRANSACTION_REINSTALL:
+    case SOLVER_TRANSACTION_UPGRADE:
+    case SOLVER_TRANSACTION_DOWNGRADE:
+      return SolverTransaction::transInstall;
+    case SOLVER_TRANSACTION_ERASE:
+    case SOLVER_TRANSACTION_REINSTALLED:
+    case SOLVER_TRANSACTION_UPGRADED:
+    case SOLVER_TRANSACTION_DOWNGRADED:
+      return SolverTransaction::transErase;
+    default:
+      Log (LOG_PLAIN) << "unknown transaction type " << std::hex << tt << endLog;
+    case SOLVER_TRANSACTION_CHANGE:
+    case SOLVER_TRANSACTION_CHANGED:
+    case SOLVER_TRANSACTION_IGNORE:
+      return SolverTransaction::transIgnore;
+    }
+};
diff --git a/libsolv.h b/libsolv.h
new file mode 100644
index 0000000..6a6e0b3
--- /dev/null
+++ b/libsolv.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2017 Jon Turney
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#ifndef LIBSOLV_H
+#define LIBSOLV_H
+
+#include "solv/pool.h"
+#include "solv/repo.h"
+#include "solv/solver.h"
+#include "PackageSpecification.h"
+#include "PackageTrust.h"
+#include "package_source.h"
+#include "package_depends.h"
+#include <map>
+#include <vector>
+
+typedef trusts package_stability_t;
+
+typedef enum
+{
+  package_binary,
+  package_source
+}
+package_type_t;
+
+// ---------------------------------------------------------------------------
+// interface to class SolverVersion
+//
+// a wrapper around a libsolv Solvable
+// ---------------------------------------------------------------------------
+
+class SolverPool;
+class SolverSolution;
+
+class SolvableVersion
+{
+ public:
+  SolvableVersion() : id(0), pool(0) {};
+  SolvableVersion(Id _id, Pool *_pool) : id(_id), pool(_pool) {};
+
+  // converted to a bool, this is true if this isn't the result of the default
+  // constructor (an 'empty' version, in some sense)
+  explicit operator bool () const { return (id != 0); }
+
+  const std::string Name () const;
+  const std::string SDesc () const;
+  // In setup-speak, 'Canonical' version means 'e:v-r', the non-decomposed version
+  const std::string Canonical_version () const;
+  // Return the dependency list
+  const PackageDepends depends() const;
+  // Return the obsoletes list
+  const PackageDepends obsoletes() const;
+  bool accessible () const;
+  package_type_t Type () const;
+  package_stability_t Stability () const;
+  // the associated source package name, if this is a binary package
+  const std::string sourcePackageName () const;
+  // the associated source package, if this is a binary package
+  SolvableVersion sourcePackage () const;
+  // where this package archive can be obtained from
+  packagesource *source() const;
+
+  // fixup spkg_id
+  void fixup_spkg_id(SolvableVersion spkg_id) const;
+
+  // utility function to compare package versions
+  static int compareVersions(const SolvableVersion &a, const SolvableVersion &b);
+
+  // comparison operators
+
+  // these are somewhat necessary as otherwise we are compared as bool values
+  bool operator == (SolvableVersion const &) const;
+  bool operator != (SolvableVersion const &) const;
+
+  // these are only well defined for versions of the same package
+  bool operator < (SolvableVersion const &) const;
+  bool operator <= (SolvableVersion const &) const;
+  bool operator > (SolvableVersion const &) const;
+  bool operator >= (SolvableVersion const &) const;
+
+ private:
+  Id id;
+  Pool *pool;
+
+  friend SolverPool;
+  friend SolverSolution;
+
+  const PackageDepends deplist(Id keyname) const;
+  Id name_id () const;
+};
+
+// ---------------------------------------------------------------------------
+// Helper class SolvRepo
+//
+// ---------------------------------------------------------------------------
+
+class SolvRepo
+{
+public:
+  typedef enum
+  {
+    priorityLow = 0,
+    priorityNormal,
+    priorityHigh,
+  } priority_t;
+  Repo *repo;
+  Repodata *data;
+  bool test;
+  void setPriority(priority_t p) { repo->priority = p; }
+};
+
+// ---------------------------------------------------------------------------
+// interface to class SolverPool
+//
+// a simplified wrapper for libsolv
+// ---------------------------------------------------------------------------
+
+class SolverPool
+{
+public:
+  SolverPool();
+  void clear();
+  SolvRepo *getRepo(const std::string &name, bool test = false);
+
+  // Utility class for passing arguments to addPackage()
+  class addPackageData
+  {
+  public:
+    std::string reponame;
+    std::string version;
+    std::string vendor;
+    std::string sdesc;
+    std::string ldesc;
+    package_stability_t stability;
+    package_type_t type;
+    packagesource archive;
+    PackageSpecification spkg;
+    SolvableVersion spkg_id;
+    PackageDepends *requires;
+    PackageDepends *obsoletes;
+  };
+
+  SolvableVersion addPackage(const std::string& pkgname,
+                             const addPackageData &pkgdata);
+
+  void internalize(void);
+  void use_test_packages(bool use_test_packages);
+
+
+private:
+  void init();
+  Id makedeps(Repo *repo, PackageDepends *requires);
+  Pool *pool;
+
+  typedef std::map<std::string, SolvRepo *> RepoList;
+  RepoList repos;
+
+  friend SolverSolution;
+};
+
+
+// ---------------------------------------------------------------------------
+// interface to class SolverTaskQueue
+//
+// This is used to contain a set of package install/uninstall tasks selected via
+// the UI to be passed to solver
+// ---------------------------------------------------------------------------
+
+class SolverTasks
+{
+ public:
+  enum task
+  {
+    taskInstall,
+    taskUninstall,
+    taskReinstall,
+    taskKeep,
+    taskSkip,
+    taskForceDistUpgrade,
+  };
+  void add(const SolvableVersion &v, task t)
+  {
+    tasks.push_back(taskList::value_type(v, t));
+  };
+  /* Create solver tasks corresponding to state of database */
+  void setTasks();
+
+ private:
+  typedef std::vector<std::pair<const SolvableVersion, task>> taskList;
+  taskList tasks;
+
+  friend SolverSolution;
+};
+
+// ---------------------------------------------------------------------------
+// SolverTransactionList
+//
+// a list of transactions output by the solver
+// ---------------------------------------------------------------------------
+
+class SolverTransaction
+{
+ public:
+  typedef enum
+  {
+    transIgnore,
+    transInstall,
+    transErase,
+  } transType;
+
+  SolverTransaction(SolvableVersion version_, transType type_) :
+  version(version_), type(type_) {};
+
+  SolvableVersion version;
+  transType type;
+};
+
+typedef std::vector<SolverTransaction> SolverTransactionList;
+
+// ---------------------------------------------------------------------------
+// interface to class SolverSolution
+//
+// A wrapper around the libsolv solver
+// ---------------------------------------------------------------------------
+
+class SolverSolution
+{
+ public:
+  SolverSolution(SolverPool &_pool);
+  ~SolverSolution();
+  void clear();
+
+  /* Reset package database to correspond to trans */
+  void trans2db() const;
+
+  /* Reset transaction list to correspond to package database */
+  void db2trans();
+
+  enum updateMode
+  {
+    keep,        // don't update
+    updateBest,  // update to best version
+    updateForce, // distupdate: downgrade if necessary to best version in repo
+  };
+  bool update(SolverTasks &tasks, updateMode update, bool use_test_packages);
+  void augmentTasks(SolverTasks &tasks);
+  void addSource(bool include_source);
+  void applyDefaultProblemSolutions();
+  std::string report() const;
+
+  const SolverTransactionList &transactions() const;
+  void dumpTransactionList() const;
+
+ private:
+  static SolverTransaction::transType type(Transaction *trans, int pos);
+  bool solve();
+  void tasksToJobs(SolverTasks &tasks, updateMode update, Queue &job);
+  void solutionToTransactionList();
+
+  SolverPool &pool;
+  Solver *solv;
+  Queue job;
+  SolverTransactionList trans;
+};
+
+#endif // LIBSOLV_H
diff --git a/main.cc b/main.cc
index 028f8de..2e57f94 100644
--- a/main.cc
+++ b/main.cc
@@ -55,6 +55,7 @@
 #include "site.h"
 #include "choose.h"
 #include "prereq.h"
+#include "confirm.h"
 #include "threebar.h"
 #include "desktop.h"
 #include "postinstallresults.h"
@@ -135,6 +136,7 @@ main_display ()
   SitePage Site;
   ChooserPage Chooser;
   PrereqPage Prereq;
+  ConfirmPage Confirm;
   DesktopSetupPage Desktop;
   PropSheet MainWindow;
 
@@ -175,6 +177,7 @@ main_display ()
   Site.Create ();
   Chooser.Create ();
   Prereq.Create ();
+  Confirm.Create ();
   Progress.Create ();
   PostInstallResults.Create ();
   Desktop.Create ();
@@ -189,6 +192,7 @@ main_display ()
   MainWindow.AddPage (&Site);
   MainWindow.AddPage (&Chooser);
   MainWindow.AddPage (&Prereq);
+  MainWindow.AddPage (&Confirm);
   MainWindow.AddPage (&Progress);
   MainWindow.AddPage (&PostInstallResults);
   MainWindow.AddPage (&Desktop);
diff --git a/package_db.cc b/package_db.cc
index cac98d7..730cb78 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -31,24 +31,50 @@
 #include "compress.h"
 
 #include "filemanip.h"
-
 #include "package_version.h"
-#include "cygpackage.h"
 #include "package_db.h"
 #include "package_meta.h"
 #include "Exception.h"
 #include "Generic.h"
 #include "LogSingleton.h"
 #include "resource.h"
+#include "libsolv.h"
+#include "csu_util/version_compare.h"
+#include "getopt++/BoolOption.h"
+
+static BoolOption MirrorOption (false, 'm', "mirror-mode", "Skip package availability check when installing from local directory (requires local directory to be clean mirror!)");
 
 using namespace std;
 
 packagedb::packagedb ()
 {
-  io_stream *db = 0;
+}
+
+void
+packagedb::init ()
+{
+  installeddbread = 0;
+  installeddbver = 0;
+  prepped = false;
+
+  packages.clear();
+  sourcePackages.clear();
+  categories.clear();
+  solver.clear();
+  solution.clear();
+  basepkg = packageversion();
+  dependencyOrderedPackages.clear();
+}
+
+void
+packagedb::read ()
+{
   if (!installeddbread)
     {
-      /* no parameters. Read in the local installation database. */
+      solver.internalize();
+
+      /* Read in the local installation database. */
+      io_stream *db = 0;
       db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
       installeddbread = 1;
       if (!db)
@@ -98,23 +124,61 @@ packagedb::packagedb ()
 		  if (!parseable)
 		    continue;
 
-		  packagemeta *pkg = findBinary (PackageSpecification(pkgname));
-		  if (!pkg)
-		    {
-		      pkg = new packagemeta (pkgname);
-		      packages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
-		    }
-
-		  packageversion binary = 
-		    cygpackage::createInstance (pkgname, f.ver,
-						package_binary);
-
-		  pkg->add_version (binary);
-		  pkg->set_installed (binary);
-		  pkg->desired = pkg->installed;
+                  SolverPool::addPackageData data;
+                  data.reponame = "_installed";
+                  data.version = f.ver;
+                  data.type = package_binary;
+
+                  // very limited information is available from installed.db, so
+                  // we put our best guesses here...
+                  data.vendor = "cygwin";
+                  data.requires = NULL;
+                  data.obsoletes = NULL;
+                  data.sdesc = "";
+                  data.ldesc = "";
+                  data.stability = TRUST_UNKNOWN;
+                  data.spkg = PackageSpecification(std::string(pkgname) + "-src", f.ver);
+
+                  // supplement this with sdesc, source, and stability
+                  // information from setup.ini, if possible...
+                  packageversion pv = findBinaryVersion(PackageSpecification(pkgname, f.ver));
+                  PackageDepends dep;
+                  PackageDepends obs;
+                  if (pv)
+                    {
+                      data.sdesc = pv.SDesc();
+                      data.archive = *pv.source();
+                      data.stability = pv.Stability();
+                      data.spkg_id = pv.sourcePackage();
+                      data.spkg = pv.sourcePackageName();
+                      dep = pv.depends();
+                      data.requires = &dep;
+                      obs = pv.obsoletes();
+                      data.obsoletes = &obs;
+                    }
+                  else
+                    // This version is no longer available.  It could
+                    // be old, or it could be a previous test release
+                    // that has been replaced by a new test release.
+                    // Try to get some info from the packagemeta.
+                    {
+                      packagemeta *pkgm = findBinary(PackageSpecification(pkgname));
+                      if (pkgm)
+                        {
+                          data.sdesc = pkgm->curr.SDesc();
+                          if (pkgm->curr
+                              && version_compare (f.ver, pkgm->curr.Canonical_version()) > 0)
+                            data.stability = TRUST_TEST;
+                        }
+                    }
+
+                  packagemeta *pkg = packagedb::addBinary (pkgname, data);
+
+                  pkg->set_installed_version (f.ver);
 
 		  if (dbver == 3)
 		    pkg->user_picked = (user_picked & 1);
+
 		}
 	      delete db;
 	      db = 0;
@@ -127,6 +191,80 @@ packagedb::packagedb ()
 	  installeddbver = dbver;
 	}
     }
+  solver.internalize();
+}
+
+/* Create the fictitious basepkg */
+void
+packagedb::makeBase()
+{
+  SolverPool::addPackageData data;
+  data.reponame = "_installed";
+  data.version = "0.0-0";
+  data.type = package_binary;
+  data.vendor = "cygwin";
+  data.sdesc = "Ficitious package that requires all Base packages";
+  data.ldesc = "Ficitious package that requires all Base packages";
+  data.obsoletes = NULL;
+  data.stability = TRUST_CURR;
+  // data.spkg = PackageSpecification();
+  // data.spkg_id = packageversion();
+
+  PackageDepends dep;
+  for (std::vector <packagemeta *>::const_iterator i = categories["Base"].begin();
+       i != categories["Base"].end(); i++)
+    {
+      packagemeta *pkg = *i;
+      PackageSpecification *spec = new PackageSpecification(pkg->name);
+      dep.push_back(spec);
+    }
+  data.requires = &dep;
+
+  basepkg = solver.addPackage("base", data);
+  /* We don't register this in packagemeta */
+}
+
+/* Add a package version to the packagedb */
+packagemeta *
+packagedb::addBinary (const std::string &pkgname,
+                      const SolverPool::addPackageData &pkgdata)
+{
+  /* If pkgname isn't already in packagedb, add a packagemeta */
+  packagemeta *pkg = findBinary (PackageSpecification(pkgname));
+  if (!pkg)
+    {
+      pkg = new packagemeta (pkgname);
+      packages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
+    }
+
+  /* Create the SolvableVersion  */
+  SolvableVersion sv = solver.addPackage(pkgname, pkgdata);
+
+  /* Register it in packagemeta */
+  pkg->add_version (sv, pkgdata);
+
+  return pkg;
+}
+
+packageversion
+packagedb::addSource (const std::string &pkgname,
+                      const SolverPool::addPackageData &pkgdata)
+{
+  /* If pkgname isn't already in packagedb, add a packagemeta */
+  packagemeta *pkg = findSource (PackageSpecification(pkgname));
+  if (!pkg)
+    {
+      pkg = new packagemeta (pkgname);
+      sourcePackages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
+    }
+
+  /* Create the SolvableVersion  */
+  SolvableVersion sv = solver.addPackage(pkgname, pkgdata);
+
+  /* Register it in packagemeta */
+  pkg->add_version (sv, pkgdata);
+
+  return sv;
 }
 
 int
@@ -201,6 +339,21 @@ packagedb::findBinary (PackageSpecification const &spec) const
   return NULL;
 }
 
+packageversion
+packagedb::findBinaryVersion (PackageSpecification const &spec) const
+{
+  packagedb::packagecollection::iterator n = packages.find(spec.packageName());
+  if (n != packages.end())
+    {
+      packagemeta & pkgm = *(n->second);
+      for (set<packageversion>::iterator i=pkgm.versions.begin();
+          i != pkgm.versions.end(); ++i)
+        if (spec.satisfies (*i))
+          return *i;
+    }
+  return packageversion();
+}
+
 packagemeta *
 packagedb::findSource (PackageSpecification const &spec) const
 {
@@ -216,15 +369,34 @@ packagedb::findSource (PackageSpecification const &spec) const
   return NULL;
 }
 
+packageversion
+packagedb::findSourceVersion (PackageSpecification const &spec) const
+{
+  packagedb::packagecollection::iterator n = sourcePackages.find(spec.packageName());
+  if (n != sourcePackages.end())
+    {
+      packagemeta & pkgm = *(n->second);
+      for (set<packageversion>::iterator i = pkgm.versions.begin();
+           i != pkgm.versions.end(); ++i)
+        if (spec.satisfies (*i))
+          return *i;
+    }
+  return packageversion();
+}
+
 /* static members */
 
 int packagedb::installeddbread = 0;
 int packagedb::installeddbver = 0;
+bool packagedb::prepped = false;
 packagedb::packagecollection packagedb::packages;
 packagedb::categoriesType packagedb::categories;
 packagedb::packagecollection packagedb::sourcePackages;
 PackageDBActions packagedb::task = PackageDB_Install;
+packageversion packagedb::basepkg;
 std::vector <packagemeta *> packagedb::dependencyOrderedPackages;
+SolverPool packagedb::solver;
+SolverSolution packagedb::solution(packagedb::solver);
 
 #include <stack>
 
@@ -312,8 +484,9 @@ ConnectedLoopFinder::visit(packagemeta *nodeToVisit)
   nodesInStronglyConnectedComponent.push(nodeToVisit);
 
   /* walk through each node */
-  PackageDepends::const_iterator dp = nodeToVisit->installed.depends()->begin();
-  while (dp != nodeToVisit->installed.depends()->end())
+  const PackageDepends deps = nodeToVisit->installed.depends();
+  PackageDepends::const_iterator dp = deps.begin();
+  while (dp != deps.end())
     {
       /* check for an installed match */
       if (checkForInstalled (*dp))
@@ -428,22 +601,12 @@ packagedb::fillMissingCategory ()
 }
 
 void
-packagedb::defaultTrust (trusts trust)
+packagedb::defaultTrust (SolverTasks &q, SolverSolution::updateMode mode, bool test)
 {
-  for (packagedb::packagecollection::iterator i = packages.begin (); i != packages.end (); ++i)
-    {
-      packagemeta & pkg = *(i->second);
-      if (pkg.installed
-            || pkg.categories.find ("Base") != pkg.categories.end ()
-            || pkg.categories.find ("Orphaned") != pkg.categories.end ())
-        {
-          pkg.desired = pkg.trustp (true, trust);
-          if (pkg.desired)
-            pkg.pick (pkg.desired.accessible() && pkg.desired != pkg.installed);
-        }
-      else
-        pkg.desired = packageversion ();
-    }
+  solution.update(q, mode, test);
+
+  // reflect that task list into packagedb
+  solution.trans2db();
 }
 
 void
@@ -501,8 +664,9 @@ packagedb::guessUserPicked()
 	continue;
 
       /* walk through each node */
-      std::vector <PackageSpecification *>::const_iterator dp = pkgm.installed.depends()->begin();
-      while (dp != pkgm.installed.depends()->end())
+      const PackageDepends deps = pkgm.installed.depends();
+      std::vector <PackageSpecification *>::const_iterator dp = deps.begin();
+      while (dp != deps.end())
 	{
 	  /* check for an installed match */
           if (checkForInstalled(*dp))
@@ -521,3 +685,66 @@ packagedb::guessUserPicked()
 	}
     }
 }
+
+void
+packagedb::fixup_source_package_ids()
+{
+  for (packagecollection::iterator i = packages.begin ();
+       i != packages.end (); ++i)
+    {
+      packagemeta &pkgm = *(i->second);
+
+      for (set<packageversion>::iterator i = pkgm.versions.begin();
+           i != pkgm.versions.end(); ++i)
+        {
+          /* If spkg_id is already known for this package, there's nothing to
+             fix. */
+          if (i->sourcePackage())
+            continue;
+
+          /* Some packages really have no source, indicated by no [sS]ource:
+             line in setup.ini, which becomes an empty source package name */
+          const std::string spkg = i->sourcePackageName();
+          if (spkg.empty())
+            continue;
+
+          /* Otherwise, we need to find the source package and fix up the source
+             package id*/
+          packageversion spkg_id = findSourceVersion(PackageSpecification(spkg,
+                                                                          i->Canonical_version()));
+
+          if (spkg_id)
+            {
+              i->fixup_spkg_id(spkg_id);
+            }
+          else
+            {
+              Log (LOG_BABBLE) << "No source package for '" << i->Name() << "' " << i->Canonical_version() << ", source package name '" << spkg << "'" << endLog;
+            }
+        }
+    }
+}
+
+void
+packagedb::prep()
+{
+  /* make packagedb ready for use for chooser */
+  if (prepped)
+    return;
+
+  makeBase();
+  read();
+  upgrade();
+  fixup_source_package_ids();
+  removeEmptyCategories();
+
+  /* XXX: this needs to be broken out somewhere where it can do progress
+     reporting, as it can take a long time... */
+  if (source == IDC_SOURCE_DOWNLOAD || source ==IDC_SOURCE_LOCALDIR)
+    packagemeta::ScanDownloadedFiles (MirrorOption);
+
+  setExistence ();
+  fillMissingCategory ();
+
+  prepped = true;
+}
diff --git a/package_db.h b/package_db.h
index 59737c0..e500e4b 100644
--- a/package_db.h
+++ b/package_db.h
@@ -57,23 +57,29 @@ typedef std::vector <packagemeta *>::iterator PackageDBConnectedIterator;
 
 */
 
+#include "libsolv.h"
 #include <PackageTrust.h>
 
 class packagedb
 {
 public:
   packagedb ();
+  void init();
   /* 0 on success */
   int flush ();
-  void upgrade ();
+  void prep();
+
   packagemeta * findBinary (PackageSpecification const &) const;
+  packageversion findBinaryVersion (PackageSpecification const &) const;
   packagemeta * findSource (PackageSpecification const &) const;
+  packageversion findSourceVersion (PackageSpecification const &spec) const;
+  packagemeta * addBinary (const std::string &pkgname, const SolverPool::addPackageData &pkgdata);
+  packageversion addSource (const std::string &pkgname, const SolverPool::addPackageData &pkgdata);
+
   PackageDBConnectedIterator connectedBegin();
   PackageDBConnectedIterator connectedEnd();
-  void fillMissingCategory();
-  void defaultTrust (trusts trust);
-  void setExistence();
-  void removeEmptyCategories();
+
+  void defaultTrust (SolverTasks &q, SolverSolution::updateMode mode, bool test);
 
   typedef std::map <std::string, packagemeta *> packagecollection;
   /* all seen binary packages */
@@ -84,12 +90,28 @@ public:
   typedef std::map <std::string, std::vector <packagemeta *>, casecompare_lt_op > categoriesType;
   static categoriesType categories;
   static PackageDBActions task;
+  /* a ficitious package that requires all packages in the Base category */
+  static packageversion basepkg;
+
+  static SolverPool solver;
+  static SolverSolution solution;
+
 private:
+  void makeBase();
+  void read();
+  void upgrade ();
+  void fixup_source_package_ids();
+  void removeEmptyCategories();
+  void fillMissingCategory();
+  void setExistence();
+  void guessUserPicked(void);
+
   static int installeddbread;	/* do we have to reread this */
   static int installeddbver;
+  static bool prepped;
+
   friend class ConnectedLoopFinder;
   static std::vector <packagemeta *> dependencyOrderedPackages;
-  void guessUserPicked(void);
 };
 
 #endif /* SETUP_PACKAGE_DB_H */
diff --git a/package_depends.cc b/package_depends.cc
index e288c0b..a03f5a0 100644
--- a/package_depends.cc
+++ b/package_depends.cc
@@ -15,19 +15,16 @@
 #include <LogSingleton.h>
 
 void
-dumpPackageDepends (PackageDepends const *currentList,
+dumpPackageDepends (PackageDepends const &currentList,
                     std::ostream &logger)
 {
-  if (currentList)
+  Log (LOG_BABBLE) << "( ";
+  PackageDepends::const_iterator i = currentList.begin();
+  while (true)
     {
-      Log (LOG_BABBLE) << "( ";
-      PackageDepends::const_iterator i = currentList->begin();
-      while (true)
-        {
-          if (i == currentList->end()) break;
-          Log (LOG_BABBLE) << **i << " ";
-          ++i;
-        }
-      Log (LOG_BABBLE) << ")";
+      if (i == currentList.end()) break;
+      Log (LOG_BABBLE) << **i << " ";
+      ++i;
     }
+  Log (LOG_BABBLE) << ")";
 }
diff --git a/package_depends.h b/package_depends.h
index af3fa01..36f7f6f 100644
--- a/package_depends.h
+++ b/package_depends.h
@@ -19,6 +19,6 @@
 
 typedef std::vector <PackageSpecification *> PackageDepends;
 
-void dumpPackageDepends (PackageDepends const *currentList, std::ostream &);
+void dumpPackageDepends (PackageDepends const &currentList, std::ostream &);
 
 #endif // PACKAGE_DEPENDS_H
diff --git a/package_meta.cc b/package_meta.cc
index af494f4..c488e35 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -35,11 +35,7 @@ using namespace std;
 /* this goes at the same time */
 #include "win32.h"
 
-
 #include "script.h"
-
-#include "package_version.h"
-#include "cygpackage.h"
 #include "package_db.h"
 
 #include <algorithm>
@@ -128,19 +124,98 @@ packagemeta::~packagemeta()
 }
 
 void
-packagemeta::add_version (packageversion & thepkg)
+packagemeta::add_version (packageversion & thepkg, const SolverPool::addPackageData &pkgdata)
+{
+  /*
+    If a packageversion for the same version number is already present,allow
+    this version to replace it.
+
+    There is a problem where multiple repos provide a package.  It's never been
+    clear which repo should win.  With this implementation, the last one added
+    will win.
+
+    We rely on this by adding packages from installed.db last.
+   */
+
+  set <packageversion>::iterator i = versions.find(thepkg);
+  if (i != versions.end())
+    {
+      versions.erase(i);
+    }
+
+  /* Add the version */
+  std::pair<std::set <packageversion>::iterator, bool> result = versions.insert (thepkg);
+
+  if (!result.second)
+    Log (LOG_PLAIN) << "Failed to add version " << thepkg.Canonical_version() << " in package " << name << endLog;
+#ifdef DEBUG
+  else
+    Log (LOG_PLAIN) << "Added version " << thepkg.Canonical_version() << " in package " << name << endLog;
+#endif
+
+  /* Record the highest version at a given stability level */
+  /* (This has to be written somewhat carefully as attributes aren't
+     internalized yet so we can't look at them) */
+  packageversion *v = NULL;
+  switch (pkgdata.stability)
+    {
+    case TRUST_CURR:
+      v = &(this->curr);
+      break;
+    case TRUST_TEST:
+      v = &(this->exp);
+      break;
+    default:
+      break;
+    }
+
+  if (v)
+    {
+      /* Any version is always greater than no version */
+      int comparison = 1;
+      if (*v)
+        comparison = SolvableVersion::compareVersions(thepkg, *v);
+
+#ifdef DEBUG
+      if ((bool)(*v))
+        Log (LOG_BABBLE) << "package " << thepkg.Name() << " comparing versions " << thepkg.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog;
+#endif
+
+      if (comparison >= 0)
+        {
+          *v = thepkg;
+        }
+    }
+}
+
+bool
+packagemeta::isBlacklisted(const packageversion &version) const
 {
-  /* todo: check return value */
-  versions.insert (thepkg);
+  for (std::set<std::string>::iterator i = version_blacklist.begin();
+       i != version_blacklist.end();
+       i++)
+    {
+      if (i->compare(version.Canonical_version()) == 0)
+        return true;
+    }
+
+  return false;
 }
 
-/* assumption: package thepkg is already in the metadata list. */
 void
-packagemeta::set_installed (packageversion & thepkg)
+packagemeta::set_installed_version (const std::string &version)
 {
-  set<packageversion>::const_iterator temp = versions.find (thepkg);
-  if (temp != versions.end())
-    installed = thepkg;
+  set<packageversion>::iterator i;
+  for (i = versions.begin(); i != versions.end(); i++)
+    {
+      if (version.compare(i->Canonical_version()) == 0)
+        {
+          installed = *i;
+
+          /* and mark as Keep */
+          desired = installed;
+        }
+    }
 }
 
 void
@@ -174,12 +249,6 @@ packagemeta::getReadableCategoryList () const
               ).visitor.result;
 }
 
-static bool
-hasSDesc(packageversion const &pkg)
-{
-  return pkg.SDesc().size();
-}
-
 static void
 parseNames (std::set<string> &parsed, std::string &option)
 {
@@ -304,11 +373,15 @@ bool packagemeta::isManuallyDeleted() const
 const std::string
 packagemeta::SDesc () const
 {
-  set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasSDesc);
-  if (i == versions.end())
-    return std::string();
-  return i->SDesc ();
-};
+  set<packageversion>::iterator i;
+  for (i = versions.begin(); i != versions.end(); i++)
+    {
+      if (i->SDesc().size())
+        return i->SDesc ();
+    }
+
+  return std::string();
+}
 
 /* Return an appropriate caption given the current action. */
 std::string 
@@ -525,13 +598,10 @@ packagemeta::logAllVersions () const
       {
 	Log (LOG_BABBLE) << "    [" << trustLabel(*i) <<
 	  "] ver=" << i->Canonical_version() << endLog;
-	if (i->depends()->size()) 
-	{
-	  std::ostream & logger = Log (LOG_BABBLE);
-	  logger << "      depends=";
-	  dumpPackageDepends(i->depends(), logger);
-	  logger << endLog;
-	}
+        std::ostream & logger = Log (LOG_BABBLE);
+        logger << "      depends=";
+        dumpPackageDepends(i->depends(), logger);
+        logger << endLog;
       }
 #if 0
     Log (LOG_BABBLE) << "      inst=" << i->
diff --git a/package_meta.h b/package_meta.h
index 8041aa1..32372e2 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -16,7 +16,8 @@
 #ifndef SETUP_PACKAGE_META_H
 #define SETUP_PACKAGE_META_H
 
-class packageversion;
+class SolvableVersion;
+typedef SolvableVersion packageversion;
 class packagemeta;
 
 #include <set>
@@ -28,7 +29,7 @@ class packagemeta;
 
 typedef std::pair<const std::string, std::vector<packagemeta *> > Category;
 
-/* NOTE: A packagemeta without 1 packageversion is invalid! */
+/* NOTE: A packagemeta without 1 version is invalid! */
 class packagemeta
 {
 public:
@@ -42,8 +43,8 @@ public:
 
   ~packagemeta ();
 
-  void add_version (packageversion &);
-  void set_installed (packageversion &);
+  void add_version (packageversion &, const SolverPool::addPackageData &);
+  void set_installed_version (const std::string &);
   void addToCategoryBase();
   bool hasNoCategories() const;
   void setDefaultCategories();
@@ -77,6 +78,11 @@ public:
     message.set (message_id, message_string);
   }
 
+  void set_version_blacklist(std::set <std::string> &_list)
+  {
+    version_blacklist = _list;
+  }
+
   std::string action_caption () const;
   packageversion trustp (bool _default, trusts const t) const
   {
@@ -130,6 +136,8 @@ public:
   packageversion curr;
   /* ditto for "test" (experimental) */
   packageversion exp;
+  /* which one is the default according to the solver */
+  packageversion default_version;
   /* Now for the user stuff :] */
   /* What version does the user want ? */
   packageversion desired;
@@ -154,6 +162,9 @@ public:
   void addScript(Script const &);
   std::vector <Script> &scripts();
 
+  /* this version is undesirable */
+  bool isBlacklisted(const packageversion &version) const;
+
 protected:
   packagemeta &operator= (packagemeta const &);
 private:
@@ -163,6 +174,8 @@ private:
 
   bool _picked; /* true if desired version is to be (re)installed */
   bool _srcpicked;
+
+  std::set <std::string> version_blacklist;
 };
 
 #endif /* SETUP_PACKAGE_META_H */
diff --git a/package_version.cc b/package_version.cc
deleted file mode 100644
index 2587fbb..0000000
--- a/package_version.cc
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (c) 2001, 2003 Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[email protected]>
- *
- */
-
-/* this is the parent class for all package operations. 
- */
-
-#include "package_version.h"
-#include "package_db.h"
-#include "package_meta.h"
-#include "LogSingleton.h"
-#include "state.h"
-#include "resource.h"
-#include <algorithm>
-#include "csu_util/version_compare.h"
-
-using namespace std;
-
-/* a default class to avoid special casing empty packageversions */
-  
-/* TODO place into the class header */
-class _defaultversion : public _packageversion
-{
-public:
-  _defaultversion()
-    {
-      // never try to free me!
-      ++references;
-    }
-  const std::string Name(){return std::string();}
-  const std::string Vendor_version() {return std::string();}
-  const std::string Package_version() {return std::string();}
-  const std::string Canonical_version() {return std::string();}
-  void setCanonicalVersion (const std::string& ) {}
-  package_type_t Type () {return package_binary;}
-  const std::string SDesc () {return std::string();}
-  void set_sdesc (const std::string& ) {}
-  const std::string LDesc () {return std::string();}
-  void set_ldesc (const std::string& ) {}
-};
-static _defaultversion defaultversion;
-
-/* the wrapper class */
-packageversion::packageversion() : data (&defaultversion)
-{
-  ++data->references;
-}
-
-/* Create from an actual package */
-packageversion::packageversion (_packageversion *pkg)
-{
-  if (pkg)
-    data = pkg;
-  else
-    data = &defaultversion;
-  ++data->references;
-}
-
-packageversion::packageversion (packageversion const &existing) : 
-data(existing.data)
-{
-  ++data->references;
-}
-
-packageversion::~packageversion() 
-{
-  if (--data->references == 0)
-    delete data;
-}
-
-packageversion &
-packageversion::operator= (packageversion const &rhs)
-{
-  ++rhs.data->references;
-  if (--data->references == 0)
-    delete data;
-  data = rhs.data;
-  return *this;
-}
-
-bool
-packageversion::operator ! () const
-{
-  return !data->Name().size();
-}
-
-packageversion::operator bool () const
-{
-  return data->Name().size();
-}
-
-bool
-packageversion::operator == (packageversion const &rhs) const
-{
-  if (this == &rhs || data == rhs.data)
-    return true;
-  else
-    return data->Name () == rhs.data->Name() && data->Canonical_version () == rhs.data->Canonical_version();
-}
-
-bool
-packageversion::operator != (packageversion const &rhs) const
-{
-  return ! (*this == rhs);
-}
-
-bool
-packageversion::operator < (packageversion const &rhs) const
-{
-  int t = casecompare(data->Name(), rhs.data->Name());
-  if (t < 0)
-    return true;
-  else if (t > 0)
-    return false;
-  else if (casecompare (data->Canonical_version(), rhs.data->Canonical_version()) < 0)
-    return true;
-  return false;
-}
-
-const std::string 
-packageversion::Name () const
-{
-  return data->Name ();
-}
-
-const std::string
-packageversion::Vendor_version() const
-{
-  return data->Vendor_version();
-}
-
-const std::string
-packageversion::Package_version() const
-{
-  return data->Package_version();
-}
-
-const std::string
-packageversion::Canonical_version() const
-{
-  return data->Canonical_version();
-}
-
-void
-packageversion::setCanonicalVersion (const std::string& ver)
-{
-  data->setCanonicalVersion (ver);
-}
-
-package_type_t
-packageversion::Type () const
-{
-  return data->Type ();
-}
-
-const std::string
-packageversion::SDesc () const
-{
-  return data->SDesc ();
-}
-
-void
-packageversion::set_sdesc (const std::string& sdesc)
-{
-  data->set_sdesc (sdesc);
-}
-
-const std::string
-packageversion::LDesc () const
-{
-  return data->LDesc ();
-}
-
-void
-packageversion::set_ldesc (const std::string& ldesc)
-{
-  data->set_ldesc (ldesc);
-}
-
-packageversion
-packageversion::sourcePackage() const
-{
-  return data->sourcePackage();
-}
-
-PackageSpecification &
-packageversion::sourcePackageSpecification () const
-{
-  return data->sourcePackageSpecification ();
-}
-
-void
-packageversion::setSourcePackageSpecification (PackageSpecification const &spec)
-{
-  data->setSourcePackageSpecification(spec);
-}
-
-PackageDepends *
-packageversion::depends()
-{
-  return &data->depends;
-}
-
-const PackageDepends *
-packageversion::depends() const
-{
-  return &data->depends;
-}
-
-packagesource *
-packageversion::source () const
-{
-  return &data->source;
-}
-
-bool
-packageversion::accessible() const
-{
-  return data->accessible();
-}
-
-
-int
-packageversion::compareVersions(const packageversion &a, const packageversion &b)
-{
-  /* Compare Vendor_version */
-  int comparison = version_compare(a.Vendor_version(), b.Vendor_version());
- 
-#if DEBUG
-  Log (LOG_BABBLE) << "vendor version comparison " << a.Vendor_version() << " and " << b.Vendor_version() << ", result was " << comparison << endLog;
-#endif
-
-  if (comparison != 0)
-    {
-      return comparison;
-    }
-
-  /* Vendor_version are tied, compare Package_version */
-#if DEBUG
-  Log (LOG_BABBLE) <<  "package version comparison " << a.Package_version() << " and " << b.Package_version() << ", result was " << comparison << endLog;
-#endif
-
-  comparison = version_compare(a.Package_version(), b.Package_version());
-  return comparison;
-}
-
-/* the parent data class */
-
-_packageversion::_packageversion ():references (0)
-{
-}
-
-_packageversion::~_packageversion ()
-{
-}
-
-PackageSpecification &
-_packageversion::sourcePackageSpecification ()
-{
-  return _sourcePackage;
-}
-
-void
-_packageversion::setSourcePackageSpecification (PackageSpecification const &spec)
-{
-  _sourcePackage = spec;
-}
-
-packageversion
-_packageversion::sourcePackage ()
-{
-  if (!sourceVersion)
-    {
-      packagedb db;
-      packagemeta * pkg;
-      pkg = db.findSource (_sourcePackage);
-      /* no valid source meta available, just return the default
-	 (blank) package version 
-	 */
-      if (!pkg)
-	return sourceVersion;
-      set<packageversion>::iterator i=pkg->versions.begin();
-      while (i != pkg->versions.end())
-	{
-	  packageversion const & ver = * i;
-          if (_sourcePackage.satisfies (ver))
-	    sourceVersion = ver;
-          ++i;
-	}
-    }
-  return sourceVersion;
-}
-
-// is archive accessible
-bool
-_packageversion::accessible() const
-{
-  // cached ?
-  if (source.Cached ())
-    return true;
-  // net access allowed?
-  if (::source == IDC_SOURCE_LOCALDIR)
-    return false;
-  // retrievable ?
-  if (source.sites.size() || source.Cached ())
-    return true;
-  // otherwise, not accessible
-  return false;
-}
diff --git a/package_version.h b/package_version.h
index 90d576d..43cf146 100644
--- a/package_version.h
+++ b/package_version.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2003 Robert Collins.
+ * Copyright (c) 2017 Jon Turney
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License as published by
@@ -9,165 +9,13 @@
  *     A copy of the GNU General Public License can be found at
  *     http://www.gnu.org/
  *
- * Written by Robert Collins  <[email protected]>
- *
- */
-
-#ifndef SETUP_PACKAGE_VERSION_H
-#define SETUP_PACKAGE_VERSION_H
-
-/* This is a package version abstrct class, that should be able to 
- * arbitrate acceess to cygwin binary packages, cygwin source package,
- * and the rpm and deb equivalents of the same.
- */
-
-/* standard binary package metadata:
- * Name (ie mutt
- * Vendor Version (ie 2.5.1)
- * Package Version (ie 16)
- * Stability 
- * Files 
  */
 
-/* For non installed files, this class can be populated via information about
- * what is available on the net, or by parsing a specific package file.
- * for installed packages, this class should represent what is currently installed,
- * - updated by what net metadata has about it.
- * i.e. the stability of this version will change simply because the net mirrors
- * now consider it old.
- */
-
-class CategoryList;
- 
-/*Required for parsing */
-#include "package_source.h"
-#include "PackageSpecification.h"
-#include "PackageTrust.h"
-#include "package_depends.h"
-
-typedef enum
-{
-  package_invalid,
-  package_old,
-  package_current,
-  package_experimental
-}
-package_stability_t;
-
-typedef enum
-{
-  package_binary,
-  package_source
-}
-package_type_t;
-
-/* A wrapper class to be copied by value that
-   references the same package.
-   Nothing is virtual, because the wrapper cannot be inherited.
-   However, as all the methods are implemented in the referenced
-   _packageversion, that class allows virtual overriding.
-   */
-
-class _packageversion;
-class packagemeta;
-
-/* This class has pointer semantics 
-   Specifically: a=b does not alter the value of *a.
-   */
-class packageversion
-{
-public:
-  packageversion (); /* creates an empty packageversion */
-  packageversion (_packageversion *); /* used when creating an instance */
-  packageversion (packageversion const &);
-  ~packageversion (); 
-  packageversion &operator= (packageversion const &);
-  bool operator ! () const; /* true if the package is invalid. (i.e.
-			       uninitialised */
-  operator bool () const; /* returns ! !() */
-  bool operator == (packageversion const &) const; /* equality */
-  bool operator != (packageversion const &) const;
-  bool operator < (packageversion const &) const;
-  bool operator <= (packageversion const &) const;
-  bool operator > (packageversion const &) const;
-  bool operator >= (packageversion const &) const;
-
-  const std::string Name () const; 
-  const std::string Vendor_version () const;
-  const std::string Package_version () const;
-  const std::string Canonical_version () const;
-  void setCanonicalVersion (const std::string& );
-  package_type_t Type () const;
-  const std::string SDesc () const;
-  void set_sdesc (const std::string& );
-  const std::string LDesc () const;
-  void set_ldesc (const std::string& );
-  packageversion sourcePackage () const;
-  PackageSpecification & sourcePackageSpecification () const;
-  void setSourcePackageSpecification (PackageSpecification const &);
-
-  /* invariant: these never return NULL */
-  PackageDepends *depends();
-  const PackageDepends *depends() const;
-
-  /* invariant: never null */
-  packagesource *source() const; /* where can we source the file from */
-
-  bool accessible () const;
-
-  /* ensure that the depends clause is satisfied */
-  int set_requirements (trusts deftrust, size_t depth = 0);
-
-  /* utility function to compare package versions */
-  static int compareVersions(const packageversion &a, const packageversion &b);
-
-private:
-  _packageversion *data; /* Invariant: * data is always valid */
-};
-
-class _packageversion
-{
-public:
-  _packageversion();
-  virtual ~_packageversion();
-  /* for list inserts/mgmt. */
-  std::string key;
-  /* name is needed here, because if we are querying a file, the data may be embedded in
-     the file */
-  virtual const std::string Name () = 0;
-  virtual const std::string Vendor_version () = 0;
-  virtual const std::string Package_version () = 0;
-  virtual const std::string Canonical_version () = 0;
-  virtual void setCanonicalVersion (const std::string& ) = 0;
-//  virtual package_stability_t Stability () = 0;
-  virtual package_type_t Type () = 0;
-  virtual const std::string SDesc () = 0;
-  virtual void set_sdesc (const std::string& ) = 0;
-  virtual const std::string LDesc () = 0;
-  virtual void set_ldesc (const std::string& ) = 0;
-  /* only semantically meaningful for binary packages */
-  /* direct link to the source package for this binary */
-  /* if multiple versions exist and the source doesn't discriminate
-     then the most recent is used 
-     */
-  virtual packageversion sourcePackage ();
-  virtual PackageSpecification & sourcePackageSpecification ();
-  virtual void setSourcePackageSpecification (PackageSpecification const &);
-
-  PackageDepends depends;
-
-  packagesource source; /* where can we source the file from */
+#ifndef PACKAGE_VERSION_H
+#define PACKAGE_VERSION_H
 
-  virtual bool accessible () const;
+#include "libsolv.h"
 
-  /* TODO: Implement me:
-     static package_meta * scan_package (io_stream *);
-   */
-  size_t references;
-protected:
-  /* only meaningful for binary packages */
-  PackageSpecification _sourcePackage;
-  packageversion sourceVersion;
-};
+typedef SolvableVersion packageversion;
 
-#endif /* SETUP_PACKAGE_VERSION_H */
+#endif // PACKAGE_VERSION_H
diff --git a/prereq.cc b/prereq.cc
index 0a46ad1..8fcd3ba 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -13,27 +13,16 @@
  *
  */
 
-#include "win32.h"
-#include <commctrl.h>
-#include <stdio.h>
-#include <io.h>
-#include <ctype.h>
-#include <process.h>
-#include <queue>
-
 #include "prereq.h"
-#include "dialog.h"
 #include "resource.h"
 #include "state.h"
-#include "propsheet.h"
 #include "threebar.h"
-#include "Generic.h"
 #include "LogSingleton.h"
 #include "ControlAdjuster.h"
 #include "package_db.h"
-#include "package_meta.h"
-#include "msg.h"
+
 #include "Exception.h"
+#include "getopt++/BoolOption.h"
 
 // Sizing information.
 static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
@@ -43,6 +32,7 @@ static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
 };
 
 extern ThreeBarProgressPage Progress;
+BoolOption IncludeSource (false, 'I', "include-source", "Automatically install source for every package installed");
 
 // ---------------------------------------------------------------------------
 // implements class PrereqPage
@@ -73,7 +63,7 @@ void
 PrereqPage::OnActivate()
 {
   // if we have gotten this far, then PrereqChecker has already run isMet
-  // and found that there were missing packages; so we can just call
+  // and found that there were problems; so we can just call
   // getUnmetString to format the results and display it
 
   std::string s;
@@ -88,53 +78,50 @@ long
 PrereqPage::OnNext ()
 {
   HWND h = GetHWND ();
+  packagedb db;
 
   if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK))
     {
       // breakage imminent!  danger, danger
       int res = MessageBox (h,
-          "The listed packages are required for packages depending on them to "
-          "work.  We strongly recommend that you allow Setup to select them."
+          "Some packages may not work properly if you continue."
           "\r\n\r\n"
-          "Are you sure you want to proceed without these packages?",
-          "WARNING - Required Packages Not Selected",
+          "Are you sure you want to proceed (NOT RECOMMENDED)?",
+          "WARNING - Unsolved Problems",
           MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2);
       if (res == IDNO)
         return -1;
       else
-        Log (LOG_PLAIN) <<
-            "NOTE!  User refused suggested missing dependencies!  "
+        {
+          Log (LOG_PLAIN) <<
+            "NOTE!  User continued with unsolved problems!  "
             "Expect some packages to give errors or not function at all." << endLog;
+          // Change the solver's transaction list to reflect the user's choices.
+          db.solution.db2trans();
+        }
     }
   else
     {
-      // add the missing requirements
-      PrereqChecker p;
-      p.selectMissing ();
+      db.solution.applyDefaultProblemSolutions();
     }
 
-  return whatNext();
-}
+  PrereqChecker p;
+  p.finalize();
 
-long
-PrereqPage::whatNext ()
-{
-  if (source == IDC_SOURCE_LOCALDIR)
-    {
-      // Next, install
-      Progress.SetActivateTask (WM_APP_START_INSTALL);
-    }
-  else
-    {
-      // Next, start download from internet
-      Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
-    }
-  return IDD_INSTATUS;
+  return IDD_CONFIRM;
 }
 
 long
 PrereqPage::OnBack ()
 {
+  // Add reinstall tasks
+  PrereqChecker p;
+  p.augment();
+
+  // Reset the package database to correspond to the solver's solution
+  packagedb db;
+  db.solution.trans2db();
+
   return IDD_CHOOSE;
 }
 
@@ -145,11 +132,7 @@ PrereqPage::OnUnattended ()
   if (unattended_mode == chooseronly)
     return -1;
 
-  // in unattended mode, add the missing requirements, then carry on to download/install
-  PrereqChecker p;
-  p.selectMissing ();
-
-  return whatNext();
+  return IDD_CONFIRM;
 }
 
 // ---------------------------------------------------------------------------
@@ -157,170 +140,55 @@ PrereqPage::OnUnattended ()
 // ---------------------------------------------------------------------------
 
 // instantiate the static members
-map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> PrereqChecker::unmet;
-map <std::string, vector <packagemeta *> > PrereqChecker::notfound;
-trusts PrereqChecker::theTrust = TRUST_CURR;
+bool PrereqChecker::use_test_packages;
+SolverTasks PrereqChecker::q;
 
-/* This function builds a list of unmet dependencies to present to the user on
-   the PrereqPage propsheet.
-
-   The data is stored in two associative maps:
-     unmet[package] = vector of packages that depend on package.
-     notfound[package-name] = vector of packages that depend on package.
-*/
 bool
 PrereqChecker::isMet ()
 {
   packagedb db;
 
-  Progress.SetText1 ("Checking prerequisites...");
+  Progress.SetText1 ("Solving dependencies...");
   Progress.SetText2 ("");
   Progress.SetText3 ("");
 
-  // clear static data each time this is called
-  unmet.clear ();
-  notfound.clear ();
-
-  // packages that need to be checked for dependencies
-  queue <packagemeta *> todo;
-
-  // go through all packages, adding desired ones to the initial work list
-  for (packagedb::packagecollection::iterator p = db.packages.begin ();
-        p != db.packages.end (); ++p)
-    {
-      if (p->second->desired)
-        todo.push (p->second);
-    }
-
-  int max = todo.size();
-  int pos = 0;
+  // Create task list corresponding to current state of package database
+  q.setTasks();
 
-  // churn through the work list
-  while (!todo.empty ())
-    {
-      // get the first package off the work list
-      packagemeta *pack = todo.front ();
-      todo.pop ();
-
-      pos++;
-      Progress.SetText2 (pack->name.c_str());
-      static char buf[100];
-      sprintf(buf, "%d %%  (%d/%d)", pos * 100 / max, pos, max);
-      Progress.SetText3(buf);
-      Progress.SetBar1(pos, max);
-
-      // Fetch the dependencies of the package. This assumes that the
-      // dependencies of all versions are all the same.
-      const PackageDepends *deps = pack->curr.depends ();
-
-      // go through the package's dependencies
-      for (PackageDepends::const_iterator d =
-            deps->begin (); d != deps->end (); ++d)
-        {
-          PackageSpecification *dep_spec = *d;
-          packagemeta *dep = db.findBinary (*dep_spec);
-
-          if (dep)
-            {
-              if (!(dep->desired && dep_spec->satisfies (dep->desired)))
-                {
-                  // we've got an unmet dependency
-                  if (unmet.find (dep) == unmet.end ())
-                    {
-                      // newly found dependency: add to worklist
-                      todo.push (dep);
-                      max++;
-                    }
-                  unmet[dep].push_back (pack);
-                }
-            }
-          else
-            {
-              // dependency on a package which doesn't have any binary versions
-              // (i.e. it is source only or doesn't exist)
-              Log (LOG_PLAIN) << "package " << pack->name << " has dependency "
-                              << dep_spec->packageName() << " we can't find" << endLog;
-              notfound[dep_spec->packageName()].push_back (pack);
-            }
-        }
-    }
-
-  return unmet.empty () && notfound.empty ();
+  // apply solver to those tasks and global state (use test or not)
+  return db.solution.update(q, SolverSolution::keep, use_test_packages);
 }
 
-/* Formats 'unmet' as a string for display to the user.  */
 void
-PrereqChecker::getUnmetString (std::string &s)
+PrereqChecker::finalize ()
 {
-  s = "";
+  augment();
 
-  {
-    map <std::string, vector <packagemeta *> >::iterator i;
-    for (i = notfound.begin(); i != notfound.end(); i++)
-      {
-        s = s + i->first
-          + "\t(not found)"
-          + "\r\n\tRequired by: ";
-        for (unsigned int j = 0; j < i->second.size(); j++)
-          {
-            s += i->second[j]->name;
-            if (j != i->second.size() - 1)
-              s += ", ";
-          }
-        s += "\r\n\r\n";
-      }
-  }
-
-  map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
-  for (i = unmet.begin(); i != unmet.end(); i++)
-    {
-      s = s + i->first->name
-	    + "\t(" + i->first->trustp (false, theTrust).Canonical_version ()
-	    + ")\r\n\t" + i->first->SDesc ()
-	    + "\r\n\tRequired by: ";
-      for (unsigned int j = 0; j < i->second.size(); j++)
-        {
-          s += i->second[j]->name;
-          if (j != i->second.size() - 1)
-            s += ", ";
-        }
-      s += "\r\n\r\n";
-    }
+  packagedb db;
+  db.solution.addSource(IncludeSource);
+  db.solution.dumpTransactionList();
 }
 
-/* Takes the keys of 'unmet' and selects them, using the current trust.  */
 void
-PrereqChecker::selectMissing ()
+PrereqChecker::augment ()
 {
   packagedb db;
+  db.solution.augmentTasks(q);
+}
 
-  // provide a default, even though this should have been set for us
-  if (!theTrust)
-    theTrust = TRUST_CURR;
+/* Formats problems and solutions as a string for display to the user.  */
+void
+PrereqChecker::getUnmetString (std::string &s)
+{
+  packagedb db;
+  s = db.solution.report();
 
-  // get each of the keys of 'unmet'
-  map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
-  for (i = unmet.begin(); i != unmet.end(); i++)
+  //
+  size_t pos = 0;
+  while ((pos = s.find("\n", pos)) != std::string::npos)
     {
-      packagemeta *pkg = i->first;
-      packageversion vers = pkg->trustp (false, theTrust);
-      pkg->desired = vers;
-      pkg->srcpick (false);
-
-      if (vers == i->first->installed)
-        {
-          pkg->pick (false);
-          Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
-               ": Selecting already-installed version " <<
-               i->first->installed.Canonical_version () << "." << endLog;
-        }
-      else
-        {
-          pkg->pick (vers.accessible ());
-          Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
-              ": Selecting version " << vers.Canonical_version () <<
-              " for installation." << endLog;
-        }
+      s.replace(pos, 1, "\r\n");
+      pos += 2;
     }
 }
 
@@ -336,11 +204,8 @@ do_prereq_check_thread(HINSTANCE h, HWND owner)
 
   if (p.isMet ())
     {
-      if (source == IDC_SOURCE_LOCALDIR)
-	Progress.SetActivateTask (WM_APP_START_INSTALL);  // install
-      else
-	Progress.SetActivateTask (WM_APP_START_DOWNLOAD); // start download
-      retval = IDD_INSTATUS;
+      p.finalize();
+      retval = IDD_CONFIRM;
     }
   else
     {
diff --git a/prereq.h b/prereq.h
index 163af6e..749d3eb 100644
--- a/prereq.h
+++ b/prereq.h
@@ -27,32 +27,27 @@ public:
   virtual long OnNext ();
   virtual long OnBack ();
   virtual long OnUnattended ();
-private:
-  long whatNext ();
 };
 
 class PrereqChecker
 {
 public:
-  // checks all dependecies, populates 'unmet'
-  // returns true if unsatisfied dependencies exist
+  // returns true if no dependency problems exist
   bool isMet ();
-  
+
   // formats 'unmet' as a string for display
   void getUnmetString (std::string &s);
-  
-  // selects/picks the needed packages that were missing
-  void selectMissing ();
-  
-  // notes the current trust (for use in selectMissing)
-  static void setTrust (trusts t) { theTrust = t; };
+
+  // finialize the transaction list
+  void finalize ();
+
+  void augment ();
+
+  static void setTestPackages (bool t) { use_test_packages = t; };
 
 private:
-  
-  // this is the actual hash_map that does all the work
-  static map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> unmet;
-  static map <std::string, vector <packagemeta *> > notfound;
-  static trusts theTrust;
+  static bool use_test_packages;
+  static SolverTasks q;
 };
 
 #endif /* SETUP_PREREQ_H */
diff --git a/res.rc b/res.rc
index 5b7d239..745b396 100644
--- a/res.rc
+++ b/res.rc
@@ -315,8 +315,9 @@ END
 
 // Right-aligned controls.
 #define SETUP_EXP_X		(SETUP_STANDARD_DIALOG_W - SETUP_KPCE_W - 7)
-#define SETUP_CURR_X		(SETUP_EXP_X - SETUP_KPCE_W - 5)
-#define SETUP_KEEP_X		(SETUP_CURR_X - SETUP_KPCE_W - 5)
+#define SETUP_SYNC_X		(SETUP_EXP_X - SETUP_KPCE_W - 5)
+#define SETUP_BEST_X		(SETUP_SYNC_X - SETUP_KPCE_W - 5)
+#define SETUP_KEEP_X		(SETUP_BEST_X - SETUP_KPCE_W - 5)
 
 // Left-aligned controls.
 #define SETUP_VIEW_X		(7)
@@ -350,9 +351,11 @@ BEGIN
                     SETUP_CLEAR_W, 14
     CONTROL         "&Keep", IDC_CHOOSE_KEEP, "Button", BS_AUTORADIOBUTTON
                     | WS_GROUP | WS_TABSTOP, SETUP_KEEP_X, 30, SETUP_KPCE_W, 14
-    CONTROL         "C&urrent", IDC_CHOOSE_CURR, "Button", BS_AUTORADIOBUTTON,
-                    SETUP_CURR_X, 30, SETUP_KPCE_W, 14
-    CONTROL         "&Test", IDC_CHOOSE_EXP, "Button", BS_AUTORADIOBUTTON,
+    CONTROL         "&Best", IDC_CHOOSE_BEST, "Button", BS_AUTORADIOBUTTON,
+                    SETUP_BEST_X, 30, SETUP_KPCE_W, 14
+    CONTROL         "&Sync", IDC_CHOOSE_SYNC, "Button", BS_AUTORADIOBUTTON,
+                    SETUP_SYNC_X, 30, SETUP_KPCE_W, 14
+    CONTROL         "&Test", IDC_CHOOSE_EXP, "Button", BS_AUTOCHECKBOX | WS_TABSTOP,
                     SETUP_EXP_X, 30, SETUP_KPCE_W, 14
     CONTROL         "", IDC_HEADSEPARATOR, "Static", SS_BLACKFRAME | SS_SUNKEN,
                     0, 28, SETUP_STANDARD_DIALOG_W, 1
@@ -378,9 +381,9 @@ BEGIN
     ICON            IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20
     LTEXT           "Resolving Dependencies",IDC_STATIC_HEADER_TITLE
                     ,7,0,258,8,NOT WS_GROUP
-    LTEXT           "The following packages are required to satisfy "
+    LTEXT           "The following problems occured trying to satisfy "
                     "dependencies.",IDC_STATIC,21,9,239,16,NOT WS_GROUP
-    CONTROL         "&Select required packages (RECOMMENDED)"
+    CONTROL         "&Accept default problem solutions"
                     ,IDC_PREREQ_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
                     7,167,225,14
     EDITTEXT        IDC_PREREQ_EDIT,7,41,303,124,WS_VSCROLL | WS_HSCROLL |
@@ -389,6 +392,22 @@ BEGIN
 
 END
 
+IDD_CONFIRM DIALOG DISCARDABLE  0, 0, SETUP_STANDARD_DIALOG_DIMS
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_CHILD | WS_VISIBLE | WS_CAPTION |
+    WS_SYSMENU
+CAPTION "Cygwin Setup - Review and confirm changes"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    CONTROL         "",IDC_HEADSEPARATOR,"Static",SS_BLACKFRAME | SS_SUNKEN,
+                    0,28,SETUP_STANDARD_DIALOG_W,1
+    ICON            IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20
+    LTEXT           "Review and confirm changes",IDC_STATIC_HEADER_TITLE
+                    ,7,0,258,8,NOT WS_GROUP
+    EDITTEXT        IDC_CONFIRM_EDIT,7,41,325,131,WS_VSCROLL | WS_HSCROLL |
+                    ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOHSCROLL |
+                    ES_AUTOVSCROLL
+END
+
 IDD_DROPPED DIALOG DISCARDABLE  0, 0, SETUP_STANDARD_DIALOG_W, 142
 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Cygwin Setup - Use dropped mirrors?"
@@ -558,10 +577,11 @@ BEGIN
     IDS_TRUSTKEEP_TOOLTIP   "Sets all packages to their currently installed "
        "version.  This is equivalent to telling setup not to make any "
        "changes to any package."
-    IDS_TRUSTCURR_TOOLTIP   "Globally select the version that is currently "
-       "considered the most stable. (RECOMMENDED)"
-    IDS_TRUSTEXP_TOOLTIP    "Globally select the most recent version, even if "
-       "that version is considered experimental or for test use by the maintainer."
+    IDS_TRUSTCURR_TOOLTIP   "Sets all packages to the best version available. "
+       "(RECOMMENDED)"
+    IDS_TRUSTSYNC_TOOLTIP   "Sets all packages to the version available from the "
+       "package respository, downgrading if necessary."
+    IDS_TRUSTEXP_TOOLTIP    "Enable test packages."
     IDS_VIEWBUTTON_TOOLTIP  "Select the package view.  This determines "
        "which packages are shown below.\r\n"
        "\r\n"
diff --git a/resource.h b/resource.h
index 70d90ca..31e080f 100644
--- a/resource.h
+++ b/resource.h
@@ -40,6 +40,7 @@
 #define IDS_INSTALLEDB_VERSION            140
 #define IDS_DOWNLOAD_INCOMPLETE_EXIT      141
 #define IDS_QUERY_CORRUPT                 142
+#define IDS_TRUSTSYNC_TOOLTIP             143
 
 // Dialogs
 
@@ -68,6 +69,7 @@
 #define IDD_POSTINSTALL                   222
 #define IDD_FILE_INUSE                    223
 #define IDD_DOWNLOAD_ERROR                224
+#define IDD_CONFIRM                       225
 
 // Bitmaps
 
@@ -120,7 +122,7 @@
 #define IDC_LISTVIEW_POS                  530
 #define IDC_CHOOSE_VIEW                   531
 #define IDC_CHOOSE_EXP                    532
-#define IDC_CHOOSE_CURR                   533
+#define IDC_CHOOSE_BEST                   533
 #define IDC_CHOOSE_LIST                   535
 #define IDC_INS_ACTION                    536
 #define IDC_ROOT_DESKTOP                  537
@@ -179,3 +181,5 @@
 #define IDC_NET_DIRECT_LEGACY             593
 #define IDC_DOWNLOAD_EDIT                 594
 #define IDC_CHOOSE_DO_SEARCH              595
+#define IDC_CHOOSE_SYNC                   596
+#define IDC_CONFIRM_EDIT                  597
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.