This is the mail archive of the cygwin-apps-cvs mailing list for the cygwin-apps project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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




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

commit bb8a17ba89743eeaa17574504395d4a9968edfc8
Merge: b7bcb7e b1902cb
Author: Jon Turney <jon.turney@dronecode.org.uk>
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  <rbtcollins@hotmail.com>
- *
- */
-
-/* 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  <rbtcollins@hotmail.com>
- *
- */
-
-#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  <rbtcollins@hotmail.com>
- *
- */
-
-/* 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  <rbtcollins@hotmail.com>
- *
- */
-
-#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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]