X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/83b880c6505a20247239d897b7387bba37942993..0327b790d40e59e1e35c6ed841308aaebbde8697:/apt-private/private-install.cc

diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc
index 107ed398e..74a2424c5 100644
--- a/apt-private/private-install.cc
+++ b/apt-private/private-install.cc
@@ -19,16 +19,16 @@
 #include <apt-pkg/macros.h>
 #include <apt-pkg/packagemanager.h>
 #include <apt-pkg/pkgcache.h>
+#include <apt-pkg/upgrade.h>
+#include <apt-pkg/install-progress.h>
 
-#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/statfs.h>
-#include <sys/statvfs.h>
 #include <algorithm>
 #include <iostream>
 #include <set>
 #include <vector>
+#include <map>
 
 #include <apt-private/acqprogress.h>
 #include <apt-private/private-install.h>
@@ -57,7 +57,8 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
       }
    }
    
-   bool Fail = false;
+   bool Hold = false;
+   bool Downgrade = false;
    bool Essential = false;
    
    // Show all the various warning indicators
@@ -65,13 +66,17 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
    ShowNew(c1out,Cache);
    if (ShwKept == true)
       ShowKept(c1out,Cache);
-   Fail |= !ShowHold(c1out,Cache);
+   Hold = !ShowHold(c1out,Cache);
    if (_config->FindB("APT::Get::Show-Upgraded",true) == true)
       ShowUpgraded(c1out,Cache);
-   Fail |= !ShowDowngraded(c1out,Cache);
+   Downgrade = !ShowDowngraded(c1out,Cache);
+
    if (_config->FindB("APT::Get::Download-Only",false) == false)
         Essential = !ShowEssential(c1out,Cache);
-   Fail |= Essential;
+
+   // All kinds of failures
+   bool Fail = (Essential || Downgrade || Hold);
+
    Stats(c1out,Cache);
 
    // Sanity check
@@ -88,20 +93,33 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
    // No remove flag
    if (Cache->DelCount() != 0 && _config->FindB("APT::Get::Remove",true) == false)
       return _error->Error(_("Packages need to be removed but remove is disabled."));
-       
+
+   // Fail safe check
+   if (_config->FindI("quiet",0) >= 2 ||
+       _config->FindB("APT::Get::Assume-Yes",false) == true)
+   {
+      if (_config->FindB("APT::Get::Force-Yes",false) == true) {
+	 _error->Warning(_("--force-yes is deprecated, use one of the options starting with --allow instead."));
+      }
+
+      if (Fail == true && _config->FindB("APT::Get::Force-Yes",false) == false) {
+	 if (Essential == true && _config->FindB("APT::Get::allow-remove-essential", false) == false)
+	    return _error->Error(_("Essential packages were removed and -y was used without --allow-remove-essential."));
+	 if (Downgrade == true && _config->FindB("APT::Get::allow-downgrades", false) == false)
+	    return _error->Error(_("Packages were downgraded and -y was used without --allow-downgrades."));
+	 if (Hold == true && _config->FindB("APT::Get::allow-change-held-packages", false) == false)
+	    return _error->Error(_("Held packages were changed and -y was used without --allow-change-held-packages."));
+      }
+   }
+
    // Run the simulator ..
    if (_config->FindB("APT::Get::Simulate") == true)
    {
       pkgSimulate PM(Cache);
 
-#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
       APT::Progress::PackageManager *progress = APT::Progress::PackageManagerProgressFactory();
       pkgPackageManager::OrderResult Res = PM.DoInstall(progress);
       delete progress;
-#else
-      int status_fd = _config->FindI("APT::Status-Fd",-1);
-      pkgPackageManager::OrderResult Res = PM.DoInstall(status_fd);
-#endif
 
       if (Res == pkgPackageManager::Failed)
 	 return false;
@@ -116,14 +134,13 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
       return false;
 
    // Create the download object
-   pkgAcquire Fetcher;
-   AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));   
+   aptAcquireWithTextStatus Fetcher;
    if (_config->FindB("APT::Get::Print-URIs", false) == true)
    {
       // force a hashsum for compatibility reasons
       _config->CndSet("Acquire::ForceHash", "md5sum");
    }
-   else if (Fetcher.Setup(&Stat, _config->FindDir("Dir::Cache::Archives")) == false)
+   else if (Fetcher.GetLock(_config->FindDir("Dir::Cache::Archives")) == false)
       return false;
 
    // Read the source list
@@ -132,7 +149,7 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
    pkgSourceList *List = Cache.GetSourceList();
    
    // Create the package manager and prepare to download
-   SPtr<pkgPackageManager> PM= _system->CreatePM(Cache);
+   std::unique_ptr<pkgPackageManager> PM(_system->CreatePM(Cache));
    if (PM->GetArchives(&Fetcher,List,&Recs) == false || 
        _error->PendingError() == true)
       return false;
@@ -174,42 +191,10 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
    if (_error->PendingError() == true)
       return false;
 
-   /* Check for enough free space, but only if we are actually going to
-      download */
-   if (_config->FindB("APT::Get::Print-URIs") == false &&
-       _config->FindB("APT::Get::Download",true) == true)
-   {
-      struct statvfs Buf;
-      std::string OutputDir = _config->FindDir("Dir::Cache::Archives");
-      if (statvfs(OutputDir.c_str(),&Buf) != 0) {
-	 if (errno == EOVERFLOW)
-	    return _error->WarningE("statvfs",_("Couldn't determine free space in %s"),
-				 OutputDir.c_str());
-	 else
-	    return _error->Errno("statvfs",_("Couldn't determine free space in %s"),
-				 OutputDir.c_str());
-      } else if (unsigned(Buf.f_bfree) < (FetchBytes - FetchPBytes)/Buf.f_bsize)
-      {
-         struct statfs Stat;
-         if (statfs(OutputDir.c_str(),&Stat) != 0
-#if HAVE_STRUCT_STATFS_F_TYPE
-             || unsigned(Stat.f_type) != RAMFS_MAGIC
-#endif
-             )
-            return _error->Error(_("You don't have enough free space in %s."),
-                OutputDir.c_str());
-      }
-   }
-   
-   // Fail safe check
-   if (_config->FindI("quiet",0) >= 2 ||
-       _config->FindB("APT::Get::Assume-Yes",false) == true)
-   {
-      if (Fail == true && _config->FindB("APT::Get::Force-Yes",false) == false)
-	 return _error->Error(_("There are problems and -y was used without --force-yes"));
-   }         
+   if (CheckFreeSpaceBeforeDownload(_config->FindDir("Dir::Cache::Archives"), (FetchBytes - FetchPBytes)) == false)
+      return false;
 
-   if (Essential == true && Safety == true)
+   if (Essential == true && Safety == true && _config->FindB("APT::Get::allow-remove-essential", false) == false)
    {
       if (_config->FindB("APT::Get::Trivial-Only",false) == true)
 	 return _error->Error(_("Trivial Only specified but this is not a trivial operation."));
@@ -330,15 +315,10 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
       }
 
       _system->UnLock();
-      
-#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
+
       APT::Progress::PackageManager *progress = APT::Progress::PackageManagerProgressFactory();
       pkgPackageManager::OrderResult Res = PM->DoInstall(progress);
       delete progress;
-#else
-      int status_fd = _config->FindI("APT::Status-Fd", -1);
-      pkgPackageManager::OrderResult Res = PM->DoInstall(status_fd);
-#endif
 
       if (Res == pkgPackageManager::Failed || _error->PendingError() == true)
 	 return false;
@@ -354,19 +334,17 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
    }
 
    std::set<std::string> const disappearedPkgs = PM->GetDisappearedPackages();
-   if (disappearedPkgs.empty() == true)
-      return true;
-
-   std::string disappear;
-   for (std::set<std::string>::const_iterator d = disappearedPkgs.begin();
-	d != disappearedPkgs.end(); ++d)
-      disappear.append(*d).append(" ");
-
-   ShowList(c1out, P_("The following package disappeared from your system as\n"
-	"all files have been overwritten by other packages:",
-	"The following packages disappeared from your system as\n"
-	"all files have been overwritten by other packages:", disappearedPkgs.size()), disappear, "");
-   c0out << _("Note: This is done automatically and on purpose by dpkg.") << std::endl;
+   if (disappearedPkgs.empty() == false)
+   {
+      ShowList(c1out, P_("The following package disappeared from your system as\n"
+	       "all files have been overwritten by other packages:",
+	       "The following packages disappeared from your system as\n"
+	       "all files have been overwritten by other packages:", disappearedPkgs.size()), disappearedPkgs,
+	    [](std::string const &Pkg) { return Pkg.empty() == false; },
+	    [](std::string const &Pkg) { return Pkg; },
+	    [](std::string const &) { return std::string(); });
+      c0out << _("Note: This is done automatically and on purpose by dpkg.") << std::endl;
+   }
 
    return true;
 }
@@ -398,11 +376,10 @@ static bool DoAutomaticRemove(CacheFile &Cache)
 
    unsigned long autoRemoveCount = 0;
    APT::PackageSet tooMuch;
-   APT::PackageList autoRemoveList;
+   SortedPackageUniverse Universe(Cache);
    // look over the cache to see what can be removed
-   for (unsigned J = 0; J < Cache->Head().PackageCount; ++J)
+   for (auto const &Pkg: Universe)
    {
-      pkgCache::PkgIterator Pkg(Cache,Cache.List[J]);
       if (Cache[Pkg].Garbage)
       {
 	 if(Pkg.CurrentVer() != 0 || Cache[Pkg].Install())
@@ -419,8 +396,6 @@ static bool DoAutomaticRemove(CacheFile &Cache)
 	 }
 	 else
 	 {
-	    if (hideAutoRemove == false && Cache[Pkg].Delete() == false)
-	       autoRemoveList.insert(Pkg);
 	    // if the package is a new install and already garbage we don't need to
 	    // install it in the first place, so nuke it instead of show it
 	    if (Cache[Pkg].Install() == true && Pkg.CurrentVer() == 0)
@@ -443,7 +418,7 @@ static bool DoAutomaticRemove(CacheFile &Cache)
       bool Changed;
       do {
 	 Changed = false;
-	 for (APT::PackageSet::const_iterator Pkg = tooMuch.begin();
+	 for (APT::PackageSet::iterator Pkg = tooMuch.begin();
 	      Pkg != tooMuch.end(); ++Pkg)
 	 {
 	    APT::PackageSet too;
@@ -480,18 +455,6 @@ static bool DoAutomaticRemove(CacheFile &Cache)
       } while (Changed == true);
    }
 
-   std::string autoremovelist, autoremoveversions;
-   if (smallList == false && autoRemoveCount != 0)
-   {
-      for (APT::PackageList::const_iterator Pkg = autoRemoveList.begin(); Pkg != autoRemoveList.end(); ++Pkg)
-      {
-	 if (Cache[Pkg].Garbage == false)
-	    continue;
-	 autoremovelist += Pkg.FullName(true) + " ";
-	 autoremoveversions += std::string(Cache[Pkg].CandVersion) + "\n";
-      }
-   }
-
    // Now see if we had destroyed anything (if we had done anything)
    if (Cache->BrokenCount() != 0)
    {
@@ -506,16 +469,25 @@ static bool DoAutomaticRemove(CacheFile &Cache)
    }
 
    // if we don't remove them, we should show them!
-   if (doAutoRemove == false && (autoremovelist.empty() == false || autoRemoveCount != 0))
+   if (doAutoRemove == false && autoRemoveCount != 0)
    {
       if (smallList == false)
+      {
+	 SortedPackageUniverse Universe(Cache);
 	 ShowList(c1out, P_("The following package was automatically installed and is no longer required:",
 	          "The following packages were automatically installed and are no longer required:",
-	          autoRemoveCount), autoremovelist, autoremoveversions);
+	          autoRemoveCount), Universe,
+	       [&Cache](pkgCache::PkgIterator const &Pkg) { return (*Cache)[Pkg].Garbage == true && (*Cache)[Pkg].Delete() == false; },
+	       &PrettyFullName, CandidateVersion(&Cache));
+      }
       else
 	 ioprintf(c1out, P_("%lu package was automatically installed and is no longer required.\n",
 	          "%lu packages were automatically installed and are no longer required.\n", autoRemoveCount), autoRemoveCount);
-      c1out << P_("Use 'apt-get autoremove' to remove it.", "Use 'apt-get autoremove' to remove them.", autoRemoveCount) << std::endl;
+      std::string autocmd = "apt autoremove";
+      if (getenv("SUDO_USER") != NULL)
+	 autocmd = "sudo " + autocmd;
+      ioprintf(c1out, P_("Use '%s' to remove it.", "Use '%s' to remove them.", autoRemoveCount), autocmd.c_str());
+      c1out << std::endl;
    }
    return true;
 }
@@ -524,23 +496,27 @@ static bool DoAutomaticRemove(CacheFile &Cache)
 static const unsigned short MOD_REMOVE = 1;
 static const unsigned short MOD_INSTALL = 2;
 
-bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache)
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache, int UpgradeMode)
+{
+   std::vector<const char*> VolatileCmdL;
+   return DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, UpgradeMode);
+}
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, std::vector<const char*> &VolatileCmdL, CacheFile &Cache, int UpgradeMode)
 {
    std::map<unsigned short, APT::VersionSet> verset;
-   return DoCacheManipulationFromCommandLine(CmdL, Cache, verset);
+   return DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, verset, UpgradeMode);
 }
-bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
-                                        std::map<unsigned short, APT::VersionSet> &verset)
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, std::vector<const char*> &VolatileCmdL, CacheFile &Cache,
+                                        std::map<unsigned short, APT::VersionSet> &verset, int UpgradeMode)
 {
-
    // Enter the special broken fixing mode if the user specified arguments
    bool BrokenFix = false;
    if (Cache->BrokenCount() != 0)
       BrokenFix = true;
 
-   SPtr<pkgProblemResolver> Fix;
+   std::unique_ptr<pkgProblemResolver> Fix(nullptr);
    if (_config->FindB("APT::Get::CallResolver", true) == true)
-      Fix = new pkgProblemResolver(Cache);
+      Fix.reset(new pkgProblemResolver(Cache));
 
    unsigned short fallback = MOD_INSTALL;
    if (strcasecmp(CmdL.FileList[0],"remove") == 0)
@@ -550,7 +526,8 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
       _config->Set("APT::Get::Purge", true);
       fallback = MOD_REMOVE;
    }
-   else if (strcasecmp(CmdL.FileList[0], "autoremove") == 0)
+   else if (strcasecmp(CmdL.FileList[0], "autoremove") == 0 ||
+	    strcasecmp(CmdL.FileList[0], "auto-remove") == 0)
    {
       _config->Set("APT::Get::AutomaticRemove", "true");
       fallback = MOD_REMOVE;
@@ -558,13 +535,27 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
 
    std::list<APT::VersionSet::Modifier> mods;
    mods.push_back(APT::VersionSet::Modifier(MOD_INSTALL, "+",
-		APT::VersionSet::Modifier::POSTFIX, APT::VersionSet::CANDIDATE));
+		APT::VersionSet::Modifier::POSTFIX, APT::CacheSetHelper::CANDIDATE));
    mods.push_back(APT::VersionSet::Modifier(MOD_REMOVE, "-",
-		APT::VersionSet::Modifier::POSTFIX, APT::VersionSet::NEWEST));
+		APT::VersionSet::Modifier::POSTFIX, APT::CacheSetHelper::NEWEST));
    CacheSetHelperAPTGet helper(c0out);
    verset = APT::VersionSet::GroupedFromCommandLine(Cache,
 		CmdL.FileList + 1, mods, fallback, helper);
 
+   for (auto const &I: VolatileCmdL)
+   {
+      pkgCache::PkgIterator const P = Cache->FindPkg(I);
+      if (P.end())
+	 continue;
+
+      // Set any version providing the .deb as the candidate.
+      for (auto Prv = P.ProvidesList(); Prv.end() == false; Prv++)
+	 Cache.GetDepCache()->SetCandidateVersion(Prv.OwnerVer());
+
+      // via cacheset to have our usual virtual handling
+      APT::VersionContainerInterface::FromPackage(&(verset[MOD_INSTALL]), Cache, P, APT::CacheSetHelper::CANDIDATE, helper);
+   }
+
    if (_error->PendingError() == true)
    {
       helper.showVirtualPackageErrors(Cache);
@@ -572,8 +563,8 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
    }
 
 
-  TryToInstall InstallAction(Cache, Fix, BrokenFix);
-  TryToRemove RemoveAction(Cache, Fix);
+  TryToInstall InstallAction(Cache, Fix.get(), BrokenFix);
+  TryToRemove RemoveAction(Cache, Fix.get());
 
    // new scope for the ActionGroup
    {
@@ -590,13 +581,8 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
 
       if (Fix != NULL && _config->FindB("APT::Get::AutoSolving", true) == true)
       {
-         for (unsigned short i = 0; order[i] != 0; ++i)
-         {
-	    if (order[i] != MOD_INSTALL)
-	       continue;
-	    InstallAction.propergateReleaseCandiateSwitching(helper.selectedByRelease, c0out);
-	    InstallAction.doAutoInstall();
-	 }
+	 InstallAction.propergateReleaseCandiateSwitching(helper.selectedByRelease, c0out);
+	 InstallAction.doAutoInstall();
       }
 
       if (_error->PendingError() == true)
@@ -617,7 +603,17 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
       if (Fix != NULL)
       {
 	 // Call the scored problem resolver
-	 Fix->Resolve(true);
+	 OpTextProgress Progress(*_config);
+	 bool const distUpgradeMode = strcmp(CmdL.FileList[0], "dist-upgrade") == 0 || strcmp(CmdL.FileList[0], "full-upgrade") == 0;
+
+	 bool resolver_fail = false;
+	 if (distUpgradeMode == true || UpgradeMode != APT::Upgrade::ALLOW_EVERYTHING)
+	    resolver_fail = APT::Upgrade::Upgrade(Cache, UpgradeMode, &Progress);
+	 else
+	    resolver_fail = Fix->Resolve(true, &Progress);
+
+	 if (resolver_fail == false && Cache->BrokenCount() == 0)
+	    return false;
       }
 
       // Now we check the state of the packages,
@@ -666,49 +662,46 @@ bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache,
 // DoInstall - Install packages from the command line			/*{{{*/
 // ---------------------------------------------------------------------
 /* Install named packages */
+struct PkgIsExtraInstalled {
+   pkgCacheFile * const Cache;
+   APT::VersionSet const * const verset;
+   PkgIsExtraInstalled(pkgCacheFile * const Cache, APT::VersionSet const * const Container) : Cache(Cache), verset(Container) {}
+   bool operator() (pkgCache::PkgIterator const &Pkg)
+   {
+        if ((*Cache)[Pkg].Install() == false)
+           return false;
+        pkgCache::VerIterator const Cand = (*Cache)[Pkg].CandidateVerIter(*Cache);
+        return verset->find(Cand) == verset->end();
+   }
+};
 bool DoInstall(CommandLine &CmdL)
 {
    CacheFile Cache;
+   std::vector<char const *> VolatileCmdL;
+   Cache.GetSourceList()->AddVolatileFiles(CmdL, &VolatileCmdL);
+
+   // then open the cache
    if (Cache.OpenForInstall() == false || 
        Cache.CheckDeps(CmdL.FileSize() != 1) == false)
       return false;
 
    std::map<unsigned short, APT::VersionSet> verset;
-
-   if(!DoCacheManipulationFromCommandLine(CmdL, Cache, verset))
+   if(!DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, verset, 0))
       return false;
 
    /* Print out a list of packages that are going to be installed extra
       to what the user asked */
+   SortedPackageUniverse Universe(Cache);
    if (Cache->InstCount() != verset[MOD_INSTALL].size())
-   {
-      std::string List;
-      std::string VersionsList;
-      for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-      {
-	 pkgCache::PkgIterator I(Cache,Cache.List[J]);
-	 if ((*Cache)[I].Install() == false)
-	    continue;
-	 pkgCache::VerIterator Cand = Cache[I].CandidateVerIter(Cache);
-
-	 if (verset[MOD_INSTALL].find(Cand) != verset[MOD_INSTALL].end())
-	    continue;
-
-	 List += I.FullName(true) + " ";
-	 VersionsList += std::string(Cache[I].CandVersion) + "\n";
-      }
-      
-      ShowList(c1out,_("The following extra packages will be installed:"),List,VersionsList);
-   }
+      ShowList(c1out, _("The following additional packages will be installed:"), Universe,
+	    PkgIsExtraInstalled(&Cache, &verset[MOD_INSTALL]),
+	    &PrettyFullName, CandidateVersion(&Cache));
 
    /* Print out a list of suggested and recommended packages */
    {
-      std::string SuggestsList, RecommendsList;
-      std::string SuggestsVersions, RecommendsVersions;
-      for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+      std::list<std::string> Recommends, Suggests, SingleRecommends, SingleSuggests;
+      for (auto const &Pkg: Universe)
       {
-	 pkgCache::PkgIterator Pkg(Cache,Cache.List[J]);
-
 	 /* Just look at the ones we want to install */
 	 if ((*Cache)[Pkg].Install() == false)
 	   continue;
@@ -720,77 +713,79 @@ bool DoInstall(CommandLine &CmdL)
 	    pkgCache::DepIterator Start;
 	    pkgCache::DepIterator End;
 	    D.GlobOr(Start,End); // advances D
+	    if (Start->Type != pkgCache::Dep::Recommends && Start->Type != pkgCache::Dep::Suggests)
+	       continue;
 
-	    // FIXME: we really should display a or-group as a or-group to the user
-	    //        the problem is that ShowList is incapable of doing this
-            std::string RecommendsOrList,RecommendsOrVersions;
-            std::string SuggestsOrList,SuggestsOrVersions;
-	    bool foundInstalledInOrGroup = false;
-	    for(;;)
 	    {
-	       /* Skip if package is  installed already, or is about to be */
-               std::string target = Start.TargetPkg().FullName(true) + " ";
-	       pkgCache::PkgIterator const TarPkg = Start.TargetPkg();
-	       if (TarPkg->SelectedState == pkgCache::State::Install ||
-		   TarPkg->SelectedState == pkgCache::State::Hold ||
-		   Cache[Start.TargetPkg()].Install())
-	       {
-		  foundInstalledInOrGroup=true;
-		  break;
-	       }
-
-	       /* Skip if we already saw it */
-	       if (int(SuggestsList.find(target)) != -1 || int(RecommendsList.find(target)) != -1)
+	       // Skip if we already saw this
+	       std::string target;
+	       for (pkgCache::DepIterator I = Start; I != D; ++I)
 	       {
-		  foundInstalledInOrGroup=true;
-		  break; 
+		  if (target.empty() == false)
+		     target.append(" | ");
+		  target.append(I.TargetPkg().FullName(true));
 	       }
+	       std::list<std::string> &Type = Start->Type == pkgCache::Dep::Recommends ? SingleRecommends : SingleSuggests;
+	       if (std::find(Type.begin(), Type.end(), target) != Type.end())
+		  continue;
+	       Type.push_back(target);
+	    }
 
-	       // this is a dep on a virtual pkg, check if any package that provides it
-	       // should be installed
-	       if(Start.TargetPkg().ProvidesList() != 0)
+	    std::list<std::string> OrList;
+	    bool foundInstalledInOrGroup = false;
+	    for (pkgCache::DepIterator I = Start; I != D; ++I)
+	    {
 	       {
-		  pkgCache::PrvIterator I = Start.TargetPkg().ProvidesList();
-		  for (; I.end() == false; ++I)
+		  // satisfying package is installed and not marked for deletion
+		  APT::VersionList installed = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::INSTALLED);
+		  if (std::find_if(installed.begin(), installed.end(),
+			   [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Delete() == false; }) != installed.end())
 		  {
-		     pkgCache::PkgIterator Pkg = I.OwnerPkg();
-		     if (Cache[Pkg].CandidateVerIter(Cache) == I.OwnerVer() && 
-			 Pkg.CurrentVer() != 0)
-			foundInstalledInOrGroup=true;
+		     foundInstalledInOrGroup = true;
+		     break;
 		  }
 	       }
 
-	       if (Start->Type == pkgCache::Dep::Suggests) 
-	       {
-		  SuggestsOrList += target;
-		  SuggestsOrVersions += std::string(Cache[Start.TargetPkg()].CandVersion) + "\n";
-	       }
-	       
-	       if (Start->Type == pkgCache::Dep::Recommends) 
 	       {
-		  RecommendsOrList += target;
-		  RecommendsOrVersions += std::string(Cache[Start.TargetPkg()].CandVersion) + "\n";
+		  // satisfying package is upgraded to/new install
+		  APT::VersionList upgrades = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::CANDIDATE);
+		  if (std::find_if(upgrades.begin(), upgrades.end(),
+			   [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Upgrade(); }) != upgrades.end())
+		  {
+		     foundInstalledInOrGroup = true;
+		     break;
+		  }
 	       }
 
-	       if (Start >= End)
-		  break;
-	       ++Start;
+	       if (OrList.empty())
+		  OrList.push_back(I.TargetPkg().FullName(true));
+	       else
+		  OrList.push_back("| " + I.TargetPkg().FullName(true));
 	    }
-	    
+
 	    if(foundInstalledInOrGroup == false)
 	    {
-	       RecommendsList += RecommendsOrList;
-	       RecommendsVersions += RecommendsOrVersions;
-	       SuggestsList += SuggestsOrList;
-	       SuggestsVersions += SuggestsOrVersions;
+	       std::list<std::string> &Type = Start->Type == pkgCache::Dep::Recommends ? Recommends : Suggests;
+	       std::move(OrList.begin(), OrList.end(), std::back_inserter(Type));
 	    }
-	       
 	 }
       }
-
-      ShowList(c1out,_("Suggested packages:"),SuggestsList,SuggestsVersions);
-      ShowList(c1out,_("Recommended packages:"),RecommendsList,RecommendsVersions);
-
+      auto always_true = [](std::string const&) { return true; };
+      auto string_ident = [](std::string const&str) { return str; };
+      auto verbose_show_candidate =
+	 [&Cache](std::string str)
+	 {
+	    if (APT::String::Startswith(str, "| "))
+	       str.erase(0, 2);
+	    pkgCache::PkgIterator const Pkg = Cache->FindPkg(str);
+	    if (Pkg.end() == true)
+	       return "";
+	    return (*Cache)[Pkg].CandVersion;
+	 };
+      ShowList(c1out,_("Suggested packages:"), Suggests,
+	    always_true, string_ident, verbose_show_candidate);
+      ShowList(c1out,_("Recommended packages:"), Recommends,
+	    always_true, string_ident, verbose_show_candidate);
    }
 
    // See if we need to prompt
@@ -798,6 +793,148 @@ bool DoInstall(CommandLine &CmdL)
    if (Cache->InstCount() == verset[MOD_INSTALL].size() && Cache->DelCount() == 0)
       return InstallPackages(Cache,false,false);
 
-   return InstallPackages(Cache,false);   
+   return InstallPackages(Cache,false);
+}
+									/*}}}*/
+
+// TryToInstall - Mark a package for installation			/*{{{*/
+void TryToInstall::operator() (pkgCache::VerIterator const &Ver) {
+   pkgCache::PkgIterator Pkg = Ver.ParentPkg();
+
+   Cache->GetDepCache()->SetCandidateVersion(Ver);
+   pkgDepCache::StateCache &State = (*Cache)[Pkg];
+
+   // Handle the no-upgrade case
+   if (_config->FindB("APT::Get::upgrade",true) == false && Pkg->CurrentVer != 0)
+      ioprintf(c1out,_("Skipping %s, it is already installed and upgrade is not set.\n"),
+	    Pkg.FullName(true).c_str());
+   // Ignore request for install if package would be new
+   else if (_config->FindB("APT::Get::Only-Upgrade", false) == true && Pkg->CurrentVer == 0)
+      ioprintf(c1out,_("Skipping %s, it is not installed and only upgrades are requested.\n"),
+	    Pkg.FullName(true).c_str());
+   else {
+      if (Fix != NULL) {
+	 Fix->Clear(Pkg);
+	 Fix->Protect(Pkg);
+      }
+      Cache->GetDepCache()->MarkInstall(Pkg,false);
+
+      if (State.Install() == false) {
+	 if (_config->FindB("APT::Get::ReInstall",false) == true) {
+	    if (Pkg->CurrentVer == 0 || Pkg.CurrentVer().Downloadable() == false)
+	       ioprintf(c1out,_("Reinstallation of %s is not possible, it cannot be downloaded.\n"),
+		     Pkg.FullName(true).c_str());
+	    else
+	       Cache->GetDepCache()->SetReInstall(Pkg, true);
+	 } else
+	    // TRANSLATORS: First string is package name, second is version
+	    ioprintf(c1out,_("%s is already the newest version (%s).\n"),
+		  Pkg.FullName(true).c_str(), Pkg.CurrentVer().VerStr());
+      }
+
+      // Install it with autoinstalling enabled (if we not respect the minial
+      // required deps or the policy)
+      if (FixBroken == false)
+	 doAutoInstallLater.insert(Pkg);
+   }
+
+   // see if we need to fix the auto-mark flag
+   // e.g. apt-get install foo
+   // where foo is marked automatic
+   if (State.Install() == false &&
+	 (State.Flags & pkgCache::Flag::Auto) &&
+	 _config->FindB("APT::Get::ReInstall",false) == false &&
+	 _config->FindB("APT::Get::Only-Upgrade",false) == false &&
+	 _config->FindB("APT::Get::Download-Only",false) == false)
+   {
+      ioprintf(c1out,_("%s set to manually installed.\n"),
+	    Pkg.FullName(true).c_str());
+      Cache->GetDepCache()->MarkAuto(Pkg,false);
+      AutoMarkChanged++;
+   }
+}
+									/*}}}*/
+bool TryToInstall::propergateReleaseCandiateSwitching(std::list<std::pair<pkgCache::VerIterator, std::string> > const &start, std::ostream &out)/*{{{*/
+{
+   for (std::list<std::pair<pkgCache::VerIterator, std::string> >::const_iterator s = start.begin();
+	 s != start.end(); ++s)
+      Cache->GetDepCache()->SetCandidateVersion(s->first);
+
+   bool Success = true;
+   // the Changed list contains:
+   //   first: "new version"
+   //   second: "what-caused the change"
+   std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > Changed;
+   for (std::list<std::pair<pkgCache::VerIterator, std::string> >::const_iterator s = start.begin();
+	 s != start.end(); ++s)
+   {
+      Changed.push_back(std::make_pair(s->first, pkgCache::VerIterator(*Cache)));
+      // We continue here even if it failed to enhance the ShowBroken output
+      Success &= Cache->GetDepCache()->SetCandidateRelease(s->first, s->second, Changed);
+   }
+   for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = Changed.begin();
+	 c != Changed.end(); ++c)
+   {
+      if (c->second.end() == true)
+	 ioprintf(out, _("Selected version '%s' (%s) for '%s'\n"),
+	       c->first.VerStr(), c->first.RelStr().c_str(), c->first.ParentPkg().FullName(true).c_str());
+      else if (c->first.ParentPkg()->Group != c->second.ParentPkg()->Group)
+      {
+	 pkgCache::VerIterator V = (*Cache)[c->first.ParentPkg()].CandidateVerIter(*Cache);
+	 ioprintf(out, _("Selected version '%s' (%s) for '%s' because of '%s'\n"), V.VerStr(),
+	       V.RelStr().c_str(), V.ParentPkg().FullName(true).c_str(), c->second.ParentPkg().FullName(true).c_str());
+      }
+   }
+   return Success;
+}
+									/*}}}*/
+void TryToInstall::doAutoInstall() {					/*{{{*/
+   for (APT::PackageSet::const_iterator P = doAutoInstallLater.begin();
+	 P != doAutoInstallLater.end(); ++P) {
+      pkgDepCache::StateCache &State = (*Cache)[P];
+      if (State.InstBroken() == false && State.InstPolicyBroken() == false)
+	 continue;
+      Cache->GetDepCache()->MarkInstall(P, true);
+   }
+   doAutoInstallLater.clear();
+}
+									/*}}}*/
+// TryToRemove - Mark a package for removal				/*{{{*/
+void TryToRemove::operator() (pkgCache::VerIterator const &Ver)
+{
+   pkgCache::PkgIterator Pkg = Ver.ParentPkg();
+
+   if (Fix != NULL)
+   {
+      Fix->Clear(Pkg);
+      Fix->Protect(Pkg);
+      Fix->Remove(Pkg);
+   }
+
+   if ((Pkg->CurrentVer == 0 && PurgePkgs == false) ||
+	 (PurgePkgs == true && Pkg->CurrentState == pkgCache::State::NotInstalled))
+   {
+      pkgCache::GrpIterator Grp = Pkg.Group();
+      pkgCache::PkgIterator P = Grp.PackageList();
+      for (; P.end() != true; P = Grp.NextPkg(P))
+      {
+	 if (P == Pkg)
+	    continue;
+	 if (P->CurrentVer != 0 || (PurgePkgs == true && P->CurrentState != pkgCache::State::NotInstalled))
+	 {
+	    // TRANSLATORS: Note, this is not an interactive question
+	    ioprintf(c1out,_("Package '%s' is not installed, so not removed. Did you mean '%s'?\n"),
+		  Pkg.FullName(true).c_str(), P.FullName(true).c_str());
+	    break;
+	 }
+      }
+      if (P.end() == true)
+	 ioprintf(c1out,_("Package '%s' is not installed, so not removed\n"),Pkg.FullName(true).c_str());
+
+      // MarkInstall refuses to install packages on hold
+      Pkg->SelectedState = pkgCache::State::Hold;
+   }
+   else
+      Cache->GetDepCache()->MarkDelete(Pkg, PurgePkgs);
 }
 									/*}}}*/