Просмотр исходного кода

* merged daniel burrows fixes for the auto-mark code

Patches applied:

 * dburrows@debian.org--2005/apt--auto-mark--0--base-0
   tag of michael.vogt@ubuntu.com--2005/apt--auto-mark--0--patch-22

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-1
   doxygenize the new automark stuff

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-2
   Automatically update package markings after every state-changing public operation, and allow users of the dep-cache to group actions into a single action.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-3
   Automatically update package markings after every state-changing public operation, and allow users of the dep-cache to group actions into a single action.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-4
   Make action groups noncopyable

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-5
   Typo fix

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-6
   Add a FromUser flag to MarkKeep.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-7
   Somehow the ActionGroup definition got duplicated; kill the duplicate.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-8
   Cancel the automatic flag on packages that are being kept only if they are garbage.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-9
   Don't clear the 'automatically installed' flag in MarkDelete.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-10
   Add a FromUser flag to MarkInstall, and fix its handling of the Auto flag.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-11
   Only clear the Auto flag on manual changes in MarkKeep.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-12
   Make changes from the internal algorithms automatic.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-13
   Use ActionGroups in algorithms that make lots of changes, and fix a compile error.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-14
   Split the sweep code into a separate routine from pkgMarkUsed

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-15
   Update another call of MarkKeep to indicate that it's automatic.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-16
   Move the mark-and-sweep code into pkgDepCache; call Sweep and document what it and Garbage are for; add a hook that can be used to generate a custom root-set function; move the big blob of regexp stuff into the custom root-set; fix the memory leak in the regexp stuff.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-17
   Make ActionGroup take a reference instead of a pointer to the cache.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-18
   Don't mark already-to-be-deleted packages as garbage, to imitate aptitude's behavior.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-19
   Update apt-get for the new auto-mark protocol.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-20
   Add a setter method for the Auto flag.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-21
   Fix the test in apt-get about what to delete.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-22
   Add a zero-argument mark-and-sweep routine and use it to do a mark-and-sweep on startup (so the garbage flags are initialized properly).

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-23
   Right, Status is 2 for new installs, not 0.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-24
   POT updates.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-25
   Actually initialize group_level to 0.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-26
   Don't make an ActionGroup in Sweep, since there's no point and it also is an infinite loop.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-27
   Add virtual hooks to control whether the garbage collector considers recommends and/or suggests to be strong links.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-28
   Call the progress methods in the right order so we don't generate nonsensical progress notifications.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-29
   Typo fix.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-30
   Make RecommendsImportant default to true in apt, too.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-31
   Add a release() method to action groups.

 * dburrows@debian.org--2005/apt--auto-mark--0--patch-32
   Add an 'autoremove' command that is synonymous to '--auto-remove remove'.
Michael Vogt лет назад: 20
Родитель
Сommit
74a05226ef
7 измененных файлов с 931 добавлено и 557 удалено
  1. 45 273
      apt-pkg/algorithms.cc
  2. 0 23
      apt-pkg/algorithms.h
  3. 297 25
      apt-pkg/depcache.cc
  4. 207 5
      apt-pkg/depcache.h
  5. 1 1
      apt-pkg/packagemanager.cc
  6. 23 12
      cmdline/apt-get.cc
  7. 358 218
      po/apt-all.pot

+ 45 - 273
apt-pkg/algorithms.cc

@@ -20,14 +20,12 @@
 #include <apt-pkg/algorithms.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/configuration.h>
-#include <apt-pkg/pkgsystem.h>
 #include <apt-pkg/version.h>
 #include <apt-pkg/sptr.h>
 
     
 #include <apti18n.h>
 #include <sys/types.h>
-#include <regex.h>
 #include <iostream>
 									/*}}}*/
 using namespace std;
@@ -224,6 +222,8 @@ void pkgSimulate::ShortBreaks()
    the necessary calculations to deal with the problems. */
 bool pkgApplyStatus(pkgDepCache &Cache)
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
    {
       if (I->VersionList == 0)
@@ -234,13 +234,13 @@ bool pkgApplyStatus(pkgDepCache &Cache)
 	  I->InstState == pkgCache::State::HoldReInstReq)
       {
 	 if (I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true)
-	    Cache.MarkKeep(I);
+	    Cache.MarkKeep(I, false, false);
 	 else
 	 {
 	    // Is this right? Will dpkg choke on an upgrade?
 	    if (Cache[I].CandidateVer != 0 &&
 		 Cache[I].CandidateVerIter(Cache).Downloadable() == true)
-	       Cache.MarkInstall(I);
+	       Cache.MarkInstall(I, false, 0, false);
 	    else
 	       return _error->Error(_("The package %s needs to be reinstalled, "
 				    "but I can't find an archive for it."),I.Name());
@@ -257,12 +257,12 @@ bool pkgApplyStatus(pkgDepCache &Cache)
 	 case pkgCache::State::HalfConfigured:
 	 if ((I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) ||
 	     I.State() != pkgCache::PkgIterator::NeedsUnpack)
-	    Cache.MarkKeep(I);
+	    Cache.MarkKeep(I, false, false);
 	 else
 	 {
 	    if (Cache[I].CandidateVer != 0 &&
 		 Cache[I].CandidateVerIter(Cache).Downloadable() == true)
-	       Cache.MarkInstall(I);
+	       Cache.MarkInstall(I, true, 0, false);
 	    else
 	       Cache.MarkDelete(I);
 	 }
@@ -288,10 +288,12 @@ bool pkgApplyStatus(pkgDepCache &Cache)
    on the result. */
 bool pkgFixBroken(pkgDepCache &Cache)
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    // Auto upgrade all broken packages
    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
       if (Cache[I].NowBroken() == true)
-	 Cache.MarkInstall(I,true);
+	 Cache.MarkInstall(I, true, 0, false);
    
    /* Fix packages that are in a NeedArchive state but don't have a
       downloadable install version */
@@ -304,7 +306,7 @@ bool pkgFixBroken(pkgDepCache &Cache)
       if (Cache[I].InstVerIter(Cache).Downloadable() == false)
 	 continue;
 
-      Cache.MarkInstall(I,true);      
+      Cache.MarkInstall(I, true, 0, false);
    }
    
    pkgProblemResolver Fix(&Cache);
@@ -321,23 +323,25 @@ bool pkgFixBroken(pkgDepCache &Cache)
  */
 bool pkgDistUpgrade(pkgDepCache &Cache)
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    /* Auto upgrade all installed packages, this provides the basis 
       for the installation */
    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
       if (I->CurrentVer != 0)
-	 Cache.MarkInstall(I,true);
+	 Cache.MarkInstall(I, true, 0, false);
 
    /* Now, auto upgrade all essential packages - this ensures that
       the essential packages are present and working */
    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
       if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
-	 Cache.MarkInstall(I,true);
+	 Cache.MarkInstall(I, true, 0, false);
    
    /* We do it again over all previously installed packages to force 
       conflict resolution on them all. */
    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
       if (I->CurrentVer != 0)
-	 Cache.MarkInstall(I,false);
+	 Cache.MarkInstall(I, false, 0, false);
 
    pkgProblemResolver Fix(&Cache);
 
@@ -349,7 +353,7 @@ bool pkgDistUpgrade(pkgDepCache &Cache)
 	 if (I->SelectedState == pkgCache::State::Hold)
 	 {
 	    Fix.Protect(I);
-	    Cache.MarkKeep(I);
+	    Cache.MarkKeep(I, false, false);
 	 }
       }
    }
@@ -364,6 +368,8 @@ bool pkgDistUpgrade(pkgDepCache &Cache)
    to install packages not marked for install */
 bool pkgAllUpgrade(pkgDepCache &Cache)
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    pkgProblemResolver Fix(&Cache);
 
    if (Cache.BrokenCount() != 0)
@@ -380,7 +386,7 @@ bool pkgAllUpgrade(pkgDepCache &Cache)
 	    continue;
       
       if (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
-	 Cache.MarkInstall(I,false);
+	 Cache.MarkInstall(I, false, 0, false);
    }
       
    return Fix.ResolveByKeep();
@@ -393,6 +399,8 @@ bool pkgAllUpgrade(pkgDepCache &Cache)
    the package is restored. */
 bool pkgMinimizeUpgrade(pkgDepCache &Cache)
 {   
+   pkgDepCache::ActionGroup group(Cache);
+
    if (Cache.BrokenCount() != 0)
       return false;
    
@@ -409,9 +417,9 @@ bool pkgMinimizeUpgrade(pkgDepCache &Cache)
 	    continue;
 
 	 // Keep it and see if that is OK
-	 Cache.MarkKeep(I);
+	 Cache.MarkKeep(I, false, false);
 	 if (Cache.BrokenCount() != 0)
-	    Cache.MarkInstall(I,false);
+	    Cache.MarkInstall(I, false, 0, false);
 	 else
 	 {
 	    // If keep didnt actually do anything then there was no change..
@@ -569,6 +577,8 @@ void pkgProblemResolver::MakeScores()
    installable */
 bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg)
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    if ((Flags[Pkg->ID] & Upgradable) == 0 || Cache[Pkg].Upgradable() == false)
       return false;
    if ((Flags[Pkg->ID] & Protected) == Protected)
@@ -577,7 +587,7 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg)
    Flags[Pkg->ID] &= ~Upgradable;
    
    bool WasKept = Cache[Pkg].Keep();
-   Cache.MarkInstall(Pkg,false);
+   Cache.MarkInstall(Pkg, false, 0, false);
 
    // This must be a virtual package or something like that.
    if (Cache[Pkg].InstVerIter(Cache).end() == true)
@@ -662,7 +672,7 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg)
    if (Fail == true)
    {
       if (WasKept == true)
-	 Cache.MarkKeep(Pkg);
+	 Cache.MarkKeep(Pkg, false, false);
       else
 	 Cache.MarkDelete(Pkg);
       return false;
@@ -689,6 +699,8 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg)
    upgrade packages to advoid problems. */
 bool pkgProblemResolver::Resolve(bool BrokenFix)
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    unsigned long Size = Cache.Head().PackageCount;
 
    // Record which packages are marked for install
@@ -704,7 +716,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 	 {
 	    if (Cache[I].InstBroken() == true && BrokenFix == true)
 	    {
-	       Cache.MarkInstall(I,false);
+	       Cache.MarkInstall(I, false, 0, false);
 	       if (Cache[I].Install() == true)
 		  Again = true;
 	    }
@@ -770,14 +782,14 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 	    pkgCache::Version *OldVer = Cache[I].InstallVer;
 	    Flags[I->ID] &= ReInstateTried;
 	    
-	    Cache.MarkInstall(I,false);
+	    Cache.MarkInstall(I, false, 0, false);
 	    if (Cache[I].InstBroken() == true || 
 		OldBreaks < Cache.BrokenCount())
 	    {
 	       if (OldVer == 0)
 		  Cache.MarkDelete(I);
 	       else
-		  Cache.MarkKeep(I);
+		  Cache.MarkKeep(I, false, false);
 	    }	    
 	    else
 	       if (Debug == true)
@@ -822,7 +834,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 		  {
 		     if (Debug == true)
 			clog << "  Or group keep for " << I.Name() << endl;
-		     Cache.MarkKeep(I);
+		     Cache.MarkKeep(I, false, false);
 		     Change = true;
 		  }
 	       }
@@ -872,7 +884,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 	       }
 	       
 	       Change = true;
-	       Cache.MarkKeep(I);		  
+	       Cache.MarkKeep(I, false, false);
 	       break;
 	    }
 	    
@@ -909,7 +921,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 		  /* See if a keep will do, unless the package is protected,
 		     then installing it will be necessary */
 		  bool Installed = Cache[I].Install();
-		  Cache.MarkKeep(I);
+		  Cache.MarkKeep(I, false, false);
 		  if (Cache[I].InstBroken() == false)
 		  {
 		     // Unwind operation will be keep now
@@ -918,7 +930,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 		     
 		     // Restore
 		     if (InOr == true && Installed == true)
-			Cache.MarkInstall(I,false);
+			Cache.MarkInstall(I, false, 0, false);
 		     
 		     if (Debug == true)
 			clog << "  Holding Back " << I.Name() << " rather than change " << Start.TargetPkg().Name() << endl;
@@ -990,7 +1002,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 		  
 		  // Restore
 		  if (InOr == true && Installed == true)
-		     Cache.MarkInstall(I,false);
+		     Cache.MarkInstall(I, false, 0, false);
 		  
 		  if (Debug == true)
 		     clog << "  Holding Back " << I.Name() << " because I can't find " << Start.TargetPkg().Name() << endl;
@@ -1035,7 +1047,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
 	       {
 		  if (Debug == true)
 		     clog << "  Fixing " << I.Name() << " via keep of " << J->Pkg.Name() << endl;
-		  Cache.MarkKeep(J->Pkg);
+		  Cache.MarkKeep(J->Pkg, false, false);
 	       }
 
 	       if (Counter > 1)
@@ -1089,6 +1101,8 @@ bool pkgProblemResolver::Resolve(bool BrokenFix)
    system was non-broken previously. */
 bool pkgProblemResolver::ResolveByKeep()
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    unsigned long Size = Cache.Head().PackageCount;
 
    if (Debug == true)      
@@ -1122,7 +1136,7 @@ bool pkgProblemResolver::ResolveByKeep()
       {
 	 if (Debug == true)
 	    clog << "Keeping package " << I.Name() << endl;
-	 Cache.MarkKeep(I);
+	 Cache.MarkKeep(I, false, false);
 	 if (Cache[I].InstBroken() == false)
 	 {
 	    K = PList - 1;
@@ -1170,7 +1184,7 @@ bool pkgProblemResolver::ResolveByKeep()
 	       {
 		  if (Debug == true)
 		     clog << "  Keeping Package " << Pkg.Name() << " due to dep" << endl;
-		  Cache.MarkKeep(Pkg);
+		  Cache.MarkKeep(Pkg, false, false);
 	       }
 	       
 	       if (Cache[I].InstBroken() == false)
@@ -1207,6 +1221,8 @@ bool pkgProblemResolver::ResolveByKeep()
 /* This is used to make sure protected packages are installed */
 void pkgProblemResolver::InstallProtect()
 {
+   pkgDepCache::ActionGroup group(Cache);
+
    for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
    {
       if ((Flags[I->ID] & Protected) == Protected)
@@ -1214,7 +1230,7 @@ void pkgProblemResolver::InstallProtect()
 	 if ((Flags[I->ID] & ToRemove) == ToRemove)
 	    Cache.MarkDelete(I);
 	 else
-	    Cache.MarkInstall(I,false);
+	    Cache.MarkInstall(I, false, 0, false);
       }
    }   
 }
@@ -1251,247 +1267,3 @@ void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List)
 }
 									/*}}}*/
 
-
-// mark a single package in Mark-and-Sweep
-void pkgMarkPackage(pkgDepCache &Cache,
-		    const pkgCache::PkgIterator &pkg,
-		    const pkgCache::VerIterator &ver,
-		    bool follow_recommends,
-		    bool follow_suggests)
-{
-   pkgDepCache::StateCache &state=Cache[pkg];
-   pkgCache::VerIterator candver=state.CandidateVerIter(Cache);
-   pkgCache::VerIterator instver=state.InstVerIter(Cache);
-
-#if 0
-   // If a package was garbage-collected but is now being marked, we
-   // should re-select it 
-   // For cases when a pkg is set to upgrade and this trigger the
-   // removal of a no-longer used dependency.  if the pkg is set to
-   // keep again later it will result in broken deps
-   if(state.Delete() && state.RemoveReason=pkgDepCache::Unused) 
-   {
-      if(ver==candver)
-	 mark_install(pkg, false, false, NULL);
-      else if(ver==pkg.CurrentVer())
-	 MarkKeep(pkg);
-      
-      instver=state.InstVerIter(*this);
-   }
-#endif
-
-   // Ignore versions other than the InstVer, and ignore packages
-   // that are already going to be removed or just left uninstalled.
-   if(!(ver==instver && !instver.end()))
-      return;
-
-   // if we are marked already we are done
-   if(state.Marked)
-      return;
-
-   //std::cout << "Setting Marked for: " << pkg.Name() << std::endl;
-   state.Marked=true;
-
-   if(!ver.end())
-   {
-     for(pkgCache::DepIterator d=ver.DependsList(); !d.end(); ++d)
-     {
-	if(d->Type==pkgCache::Dep::Depends ||
-	   d->Type==pkgCache::Dep::PreDepends ||
-	   (follow_recommends &&
-	    d->Type==pkgCache::Dep::Recommends) ||
-	   (follow_suggests &&
-	    d->Type==pkgCache::Dep::Suggests))
-        {
-	   // Try all versions of this package.
-	   for(pkgCache::VerIterator V=d.TargetPkg().VersionList(); 
-	       !V.end(); ++V)
-	   {
-	      if(_system->VS->CheckDep(V.VerStr(),d->CompareOp, d.TargetVer()))
-	      {
-		 pkgMarkPackage(Cache, V.ParentPkg(), V, 
-				follow_recommends, follow_suggests);
-	      }
-	   }
-	   // Now try virtual packages
-	   for(pkgCache::PrvIterator prv=d.TargetPkg().ProvidesList(); 
-	       !prv.end(); ++prv)
-	   {
-	      if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp, 
-				       d.TargetVer()))
-	      {
-		 pkgMarkPackage(Cache, prv.OwnerPkg(), prv.OwnerVer(),
-				follow_recommends, follow_suggests);
-	      }
-	   }
-	}
-     }
-   }
-}
-
-
-// Helper for APT::NeverAutoRemove, always include the packages matching
-// this regexp into the root-set
-inline bool 
-pkgMarkAlwaysInclude(pkgCache::PkgIterator p, vector<regex_t*> alwaysMark)
-{
-   for(unsigned int i=0;i<alwaysMark.size();i++)
-      if (regexec(alwaysMark[i],p.Name(),0,0,0) == 0)
-	 return true;
-
-   return false;
-}
-
-bool pkgMarkUsed(pkgDepCache &Cache)
-{
-   InRootSetFunc f;
-   return pkgMarkUsed(Cache, f);
-}
-
-// the main mark algorithm
-bool pkgMarkUsed(pkgDepCache &Cache, InRootSetFunc &userFunc)
-{
-   bool follow_recommends;
-   bool follow_suggests;
-
-   // init the states
-   for(pkgCache::PkgIterator p=Cache.PkgBegin(); !p.end(); ++p)
-   {
-      Cache[p].Marked=false;
-      Cache[p].Garbage=false;
-
-      // debug output
-      if(_config->FindB("Debug::pkgAutoRemove",false) 
-	 && Cache[p].Flags & pkgCache::Flag::Auto)
-  	 std::clog << "AutoDep: " << p.Name() << std::endl;
-   }
-
-   // init vars
-   follow_recommends=_config->FindB("APT::AutoRemove::RecommendsImportant",false);
-   follow_suggests=_config->FindB("APT::AutoRemove::SuggestsImportant", false);
-
-
-   // init the "NeverAutoRemove" variable
-   vector<regex_t *> neverAutoRemoveRegexp;
-   Configuration::Item const *Opts;
-   Opts = _config->Tree("APT::NeverAutoRemove");
-   if (Opts != 0 && Opts->Child != 0)
-   {
-      Opts = Opts->Child;
-      for (; Opts != 0; Opts = Opts->Next)
-      {
-	 if (Opts->Value.empty() == true)
-	    continue;
-
-	 regex_t *p = new regex_t;
-	 if(regcomp(p,Opts->Value.c_str(),
-		    REG_EXTENDED | REG_ICASE |  REG_NOSUB) != 0)
-	 {
-	    regfree(p);
-	    for(unsigned int i=0;i<neverAutoRemoveRegexp.size();i++)
-	       regfree(neverAutoRemoveRegexp[i]);
-	    return _error->Error("Regex compilation error for APT::NeverAutoRemove");
-	 }
-	 neverAutoRemoveRegexp.push_back(p);
-      }
-   }
-
-
-   // do the mark part, this is the core bit of the algorithm
-   for(pkgCache::PkgIterator p=Cache.PkgBegin(); !p.end(); ++p)
-   {
-      if( userFunc.InRootSet(p) ||
-	   pkgMarkAlwaysInclude(p, neverAutoRemoveRegexp) ||
-	 !(Cache[p].Flags & pkgCache::Flag::Auto) ||
-	  (p->Flags & pkgCache::Flag::Essential))
-          
-      {
-	 // the package is installed (and set to keep)
-	 if(Cache[p].Keep() && !p.CurrentVer().end())
-	    pkgMarkPackage(Cache, p, p.CurrentVer(),
-			   follow_recommends, follow_suggests);
-	 // the package is to be installed 
-	 else if(Cache[p].Install())
-	    pkgMarkPackage(Cache, p, Cache[p].InstVerIter(Cache),
-			   follow_recommends, follow_suggests);
-      }
-   }
-
-
-   // do the sweep
-   for(pkgCache::PkgIterator p=Cache.PkgBegin(); !p.end(); ++p)
-  {
-     pkgDepCache::StateCache &state=Cache[p];
-
-     // if it is not marked and it is installed, it's garbage 
-     if(!state.Marked && !p.CurrentVer().end())
-     {
-	state.Garbage=true;
-	if(_config->FindB("Debug::pkgAutoRemove",false))
-	   std::cout << "Garbage: " << p.Name() << std::endl;
-     
-#if 0   // mvo: the below bits still needs to be ported
-
-	// Be sure not to re-delete already deleted packages.
-	if(delete_unused && (!p.CurrentVer().end() || state.Install()) &&
-	   !state.Delete())
-	{
-	      bool do_delete=true;
-
-	      // If the package is being upgraded, check if we're
-	      // losing a versioned dep.  If the dependency matches
-	      // the previous version and not the new version, keep
-	      // the package back instead of removing it.
-	      if(!p.CurrentVer().end() && state.Install())
-		{
-		  const char *vs=p.CurrentVer().VerStr();
-
-		  // Check direct revdeps only.  THIS ASSUMES NO
-		  // VERSIONED PROVIDES, but Debian probably won't
-		  // have them for ages if ever.
-		  for(pkgCache::DepIterator revdep=p.RevDependsList();
-		      !revdep.end(); ++revdep)
-		    {
-		      pkgCache::PkgIterator depender=revdep.ParentPkg();
-		      // Find which version of the depending package
-		      // will be installed.
-		      pkgCache::VerIterator instver=(*this)[depender].InstVerIter(*this);
-
-		      // Only pay attention to strong positive
-		      // dependencies whose parents will be installed.
-		      if(revdep.ParentVer()==instver &&
-			 (revdep->Type==pkgCache::Dep::Depends ||
-			  revdep->Type==pkgCache::Dep::PreDepends ||
-			  (revdep->Type==pkgCache::Dep::Recommends &&
-			   follow_recommends)))
-			{
-			  // If the previous version matched, cancel the
-			  // deletion.  (note that I assume that the new
-			  // version does NOT match; otherwise it would
-			  // not be unused!)
-			  if(_system->VS->CheckDep(vs,
-						   revdep->CompareOp,
-						   revdep.TargetVer()))
-			    {
-			      mark_keep(p, false, false, undo);
-			      do_delete=false;
-			      break;
-			    }
-			}
-		    }
-		}
-
-	      if(do_delete)
-		mark_delete(p, false, true, undo);
-	    }
-#endif
-     }
-  }
-
-   // cleanup
-   for(unsigned int i=0;i<neverAutoRemoveRegexp.size();i++)
-      regfree(neverAutoRemoveRegexp[i]);
-   
-
-   return true;
-}

+ 0 - 23
apt-pkg/algorithms.h

@@ -132,28 +132,5 @@ bool pkgAllUpgrade(pkgDepCache &Cache);
 bool pkgMinimizeUpgrade(pkgDepCache &Cache);
 
 void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List);
-
-
-// class that can be subclassed by the client to bring in
-// certain own packages into the root set (if the client returns
-// True the package will be considered as part of the root set)
-class InRootSetFunc
-{
- public:
-   virtual bool InRootSet(const pkgCache::PkgIterator &pkg) {return false;};
-   virtual ~InRootSetFunc() {};
-};
-
-
-// Mark all reachable packages with "pkgDepCache::StateCache.Marked=1"
-// the root-set are all essential packages+everything that was not 
-// installed automatically
-//
-// If InRootSetFunc is set, it will be called for each PkgIterator. This
-// is usefull for clients that have there own idea about the root-set
-// 
-// Everything that is not reach can be removed
-bool pkgMarkUsed(pkgDepCache &Cache);
-bool pkgMarkUsed(pkgDepCache &Cache, InRootSetFunc &f);
 		     
 #endif

+ 297 - 25
apt-pkg/depcache.cc

@@ -19,18 +19,47 @@
 
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/configuration.h>
+#include <apt-pkg/pkgsystem.h>
 #include <apt-pkg/tagfile.h>
 
 #include <iostream>
 #include <sstream>    
 #include <apti18n.h>    
-									/*}}}*/
+
+pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) :
+  cache(cache), released(false)
+{
+  ++cache.group_level;
+}
+
+void pkgDepCache::ActionGroup::release()
+{
+  if(!released)
+    {
+      if(cache.group_level == 0)
+	std::cerr << "W: Unbalanced action groups, expect badness" << std::endl;
+      else
+	{
+	  --cache.group_level;
+
+	  if(cache.group_level == 0)
+	    cache.MarkAndSweep();
+	}
+
+      released = false;
+    }
+}
+
+pkgDepCache::ActionGroup::~ActionGroup()
+{
+  release();
+}
 
 // DepCache::pkgDepCache - Constructors					/*{{{*/
 // ---------------------------------------------------------------------
 /* */
 pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) :
-                Cache(pCache), PkgState(0), DepState(0)
+  group_level(0), Cache(pCache), PkgState(0), DepState(0)
 {
    delLocalPolicy = 0;
    LocalPolicy = Plcy;
@@ -53,6 +82,10 @@ pkgDepCache::~pkgDepCache()
 /* This allocats the extension buffers and initializes them. */
 bool pkgDepCache::Init(OpProgress *Prog)
 {
+   // Suppress mark updates during this operation (just in case) and
+   // run a mark operation when Init terminates.
+   ActionGroup actions(*this);
+
    delete [] PkgState;
    delete [] DepState;
    PkgState = new StateCache[Head().PackageCount];
@@ -100,7 +133,7 @@ bool pkgDepCache::Init(OpProgress *Prog)
 
    if(Prog != 0)
       Prog->Done();
-   
+
    return true;
 } 
 									/*}}}*/
@@ -161,7 +194,7 @@ bool pkgDepCache::writeStateFile(OpProgress *prog)
 
       if(PkgState[pkg->ID].Flags & Flag::Auto) {
 	 if(_config->FindB("Debug::pkgAutoRemove",false))
-	    std::clog << "AutoInstal: " << pkg.Name() << std::endl;
+	    std::clog << "AutoInstall: " << pkg.Name() << std::endl;
 	 ostr.str(string(""));
 	 ostr << "Package: " << pkg.Name() 
 	      << "\nAuto-Installed: 1\n\n";
@@ -522,16 +555,16 @@ void pkgDepCache::Update(OpProgress *Prog)
       AddStates(I);
    }
 
-   readStateFile(Prog);
-
    if (Prog != 0)      
       Prog->Progress(Done);
+
+   readStateFile(Prog);
 }
 									/*}}}*/
 // DepCache::Update - Update the deps list of a package	   		/*{{{*/
 // ---------------------------------------------------------------------
 /* This is a helper for update that only does the dep portion of the scan. 
-   It is mainly ment to scan reverse dependencies. */
+   It is mainly meant to scan reverse dependencies. */
 void pkgDepCache::Update(DepIterator D)
 {
    // Update the reverse deps
@@ -583,7 +616,7 @@ void pkgDepCache::Update(PkgIterator const &Pkg)
 // DepCache::MarkKeep - Put the package in the keep state		/*{{{*/
 // ---------------------------------------------------------------------
 /* */
-void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft)
+void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser)
 {
    // Simplifies other routines.
    if (Pkg.end() == true)
@@ -595,6 +628,9 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft)
        Pkg.CurrentVer().Downloadable() == false)
       return;
    
+   /** \todo Can this be moved later in the method? */
+   ActionGroup group(*this);
+
    /* We changed the soft state all the time so the UI is a bit nicer
       to use */
    StateCache &P = PkgState[Pkg->ID];
@@ -611,7 +647,8 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft)
    if (Pkg->VersionList == 0)
       return;
    
-   P.Flags &= ~Flag::Auto;
+   if(FromUser && !P.Marked)
+     P.Flags &= ~Flag::Auto;
    RemoveSizes(Pkg);
    RemoveStates(Pkg);
 
@@ -637,6 +674,8 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge)
    if (Pkg.end() == true)
       return;
 
+   ActionGroup group(*this);
+
    // Check that it is not already marked for delete
    StateCache &P = PkgState[Pkg->ID];
    P.iFlags &= ~(AutoKept | Purge);
@@ -659,8 +698,6 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge)
    else
       P.Mode = ModeDelete;
    P.InstallVer = 0;
-   // This was not inverted before, but I think it should be
-   P.Flags &= ~Flag::Auto;
 
    AddStates(Pkg);   
    Update(Pkg);
@@ -671,7 +708,7 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge)
 // ---------------------------------------------------------------------
 /* */
 void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
-			      unsigned long Depth)
+			      unsigned long Depth, bool FromUser)
 {
    if (Depth > 100)
       return;
@@ -680,6 +717,8 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
    if (Pkg.end() == true)
       return;
    
+   ActionGroup group(*this);
+
    /* Check that it is not already marked for install and that it can be 
       installed */
    StateCache &P = PkgState[Pkg->ID];
@@ -688,7 +727,7 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
 	P.CandidateVer == (Version *)Pkg.CurrentVer()))
    {
       if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0)
-	 MarkKeep(Pkg);
+	 MarkKeep(Pkg, false, FromUser);
       return;
    }
 
@@ -708,9 +747,20 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
    
    P.Mode = ModeInstall;
    P.InstallVer = P.CandidateVer;
-   // invert the auto-flag only for new installs, not for upgrades
-   if(P.Status == 0)
-      P.Flags &= ~Flag::Auto;
+
+   if(FromUser)
+     {
+       // Set it to manual if it's a new install or cancelling the
+       // removal of a garbage package.
+       if(P.Status == 2 || (!Pkg.CurrentVer().end() && !P.Marked))
+	 P.Flags &= ~Flag::Auto;
+     }
+   else
+     {
+       // Set it to auto if this is a new install.
+       if(P.Status == 2)
+	 P.Flags |= Flag::Auto;
+     }
    if (P.CandidateVer == (Version *)Pkg.CurrentVer())
       P.Mode = ModeKeep;
        
@@ -788,13 +838,7 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
 	 }
 	 
 	 if (InstPkg.end() == false)
-	 {
-	    MarkInstall(InstPkg,true,Depth + 1);
-
-	    // Set the autoflag, after MarkInstall because MarkInstall unsets it
-	    if (P->CurrentVer == 0)
-	       PkgState[InstPkg->ID].Flags |= Flag::Auto;
-	 }
+	   MarkInstall(InstPkg, true, Depth + 1, false);
 	 
 	 continue;
       }
@@ -809,7 +853,6 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
 	    PkgIterator Pkg = Ver.ParentPkg();
       
 	    MarkDelete(Pkg);
-	    PkgState[Pkg->ID].Flags |= Flag::Auto;
 	 }
 	 continue;
       }      
@@ -821,6 +864,8 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
 /* */
 void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To)
 {
+   ActionGroup group(*this);
+
    RemoveSizes(Pkg);
    RemoveStates(Pkg);
    
@@ -839,9 +884,11 @@ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To)
 /* */
 void pkgDepCache::SetCandidateVersion(VerIterator TargetVer)
 {
+   ActionGroup group(*this);
+
    pkgCache::PkgIterator Pkg = TargetVer.ParentPkg();
    StateCache &P = PkgState[Pkg->ID];
-   
+
    RemoveSizes(Pkg);
    RemoveStates(Pkg);
 
@@ -854,6 +901,18 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer)
    Update(Pkg);
    AddSizes(Pkg);
 }
+
+void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto)
+{
+  StateCache &state = PkgState[Pkg->ID];
+
+  ActionGroup group(*this);
+
+  if(Auto)
+    state.Flags |= Flag::Auto;
+  else
+    state.Flags &= ~Flag::Auto;
+}
 									/*}}}*/
 // StateCache::Update - Compute the various static display things	/*{{{*/
 // ---------------------------------------------------------------------
@@ -944,3 +1003,216 @@ bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep)
    return Dep.IsCritical();
 }
 									/*}}}*/
+
+pkgDepCache::DefaultRootSetFunc::DefaultRootSetFunc()
+  : constructedSuccessfully(false)
+{
+  Configuration::Item const *Opts;
+  Opts = _config->Tree("APT::NeverAutoRemove");
+  if (Opts != 0 && Opts->Child != 0)
+    {
+      Opts = Opts->Child;
+      for (; Opts != 0; Opts = Opts->Next)
+	{
+	  if (Opts->Value.empty() == true)
+	    continue;
+
+	  regex_t *p = new regex_t;
+	  if(regcomp(p,Opts->Value.c_str(),
+		     REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
+	    {
+	      regfree(p);
+	      delete p;
+	      _error->Error("Regex compilation error for APT::NeverAutoRemove");
+	      return;
+	    }
+
+	  rootSetRegexp.push_back(p);
+	}
+    }
+
+  constructedSuccessfully = true;
+}
+
+pkgDepCache::DefaultRootSetFunc::~DefaultRootSetFunc()
+{
+  for(unsigned int i = 0; i < rootSetRegexp.size(); i++)
+    {
+      regfree(rootSetRegexp[i]);
+      delete rootSetRegexp[i];
+    }
+}
+
+
+bool pkgDepCache::DefaultRootSetFunc::InRootSet(const pkgCache::PkgIterator &pkg)
+{
+   for(unsigned int i = 0; i < rootSetRegexp.size(); i++)
+      if (regexec(rootSetRegexp[i], pkg.Name(), 0, 0, 0) == 0)
+	 return true;
+
+   return false;
+}
+
+pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc()
+{
+  DefaultRootSetFunc *f = new DefaultRootSetFunc;
+  if(f->wasConstructedSuccessfully())
+    return f;
+  else
+    {
+      delete f;
+      return NULL;
+    }
+}
+
+bool pkgDepCache::MarkFollowsRecommends()
+{
+  return _config->FindB("APT::AutoRemove::RecommendsImportant", true);
+}
+
+bool pkgDepCache::MarkFollowsSuggests()
+{
+  return _config->FindB("APT::AutoRemove::SuggestsImportant", false);
+}
+
+// the main mark algorithm
+bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc)
+{
+   bool follow_recommends;
+   bool follow_suggests;
+
+   // init the states
+   for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+   {
+      PkgState[p->ID].Marked  = false;
+      PkgState[p->ID].Garbage = false;
+
+      // debug output
+      if(_config->FindB("Debug::pkgAutoRemove",false) 
+	 && PkgState[p->ID].Flags & Flag::Auto)
+  	 std::clog << "AutoDep: " << p.Name() << std::endl;
+   }
+
+   // init vars
+   follow_recommends = MarkFollowsRecommends();
+   follow_suggests   = MarkFollowsSuggests();
+
+
+
+   // do the mark part, this is the core bit of the algorithm
+   for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+   {
+      if(!(PkgState[p->ID].Flags & Flag::Auto) ||
+	  (p->Flags & Flag::Essential) ||
+	  userFunc.InRootSet(p))
+          
+      {
+	 // the package is installed (and set to keep)
+	 if(PkgState[p->ID].Keep() && !p.CurrentVer().end())
+	    MarkPackage(p, p.CurrentVer(),
+			follow_recommends, follow_suggests);
+	 // the package is to be installed 
+	 else if(PkgState[p->ID].Install())
+	    MarkPackage(p, PkgState[p->ID].InstVerIter(*this),
+			follow_recommends, follow_suggests);
+      }
+   }
+
+   return true;
+}
+
+// mark a single package in Mark-and-Sweep
+void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver,
+			      bool follow_recommends,
+			      bool follow_suggests)
+{
+   pkgDepCache::StateCache &state = PkgState[pkg->ID];
+   VerIterator candver            = state.CandidateVerIter(*this);
+   VerIterator instver            = state.InstVerIter(*this);
+
+#if 0
+   // If a package was garbage-collected but is now being marked, we
+   // should re-select it 
+   // For cases when a pkg is set to upgrade and this trigger the
+   // removal of a no-longer used dependency.  if the pkg is set to
+   // keep again later it will result in broken deps
+   if(state.Delete() && state.RemoveReason = Unused) 
+   {
+      if(ver==candver)
+	 mark_install(pkg, false, false, NULL);
+      else if(ver==pkg.CurrentVer())
+	 MarkKeep(pkg, false, false);
+      
+      instver=state.InstVerIter(*this);
+   }
+#endif
+
+   // Ignore versions other than the InstVer, and ignore packages
+   // that are already going to be removed or just left uninstalled.
+   if(!(ver == instver && !instver.end()))
+      return;
+
+   // if we are marked already we are done
+   if(state.Marked)
+      return;
+
+   //std::cout << "Setting Marked for: " << pkg.Name() << std::endl;
+   state.Marked=true;
+
+   if(!ver.end())
+   {
+     for(DepIterator d = ver.DependsList(); !d.end(); ++d)
+     {
+	if(d->Type == Dep::Depends ||
+	   d->Type == Dep::PreDepends ||
+	   (follow_recommends &&
+	    d->Type == Dep::Recommends) ||
+	   (follow_suggests &&
+	    d->Type == Dep::Suggests))
+        {
+	   // Try all versions of this package.
+	   for(VerIterator V = d.TargetPkg().VersionList(); 
+	       !V.end(); ++V)
+	   {
+	      if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer()))
+	      {
+		 MarkPackage(V.ParentPkg(), V, 
+			     follow_recommends, follow_suggests);
+	      }
+	   }
+	   // Now try virtual packages
+	   for(PrvIterator prv=d.TargetPkg().ProvidesList(); 
+	       !prv.end(); ++prv)
+	   {
+	      if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp, 
+				       d.TargetVer()))
+	      {
+		 MarkPackage(prv.OwnerPkg(), prv.OwnerVer(),
+			     follow_recommends, follow_suggests);
+	      }
+	   }
+	}
+     }
+   }
+}
+
+bool pkgDepCache::Sweep()
+{
+   // do the sweep
+   for(PkgIterator p=PkgBegin(); !p.end(); ++p)
+  {
+     StateCache &state=PkgState[p->ID];
+
+     // if it is not marked and it is installed, it's garbage 
+     if(!state.Marked && (!p.CurrentVer().end() || state.Install()) &&
+	!state.Delete())
+     {
+	state.Garbage=true;
+	if(_config->FindB("Debug::pkgAutoRemove",false))
+	   std::cout << "Garbage: " << p.Name() << std::endl;
+     }
+  }   
+
+   return true;
+}

+ 207 - 5
apt-pkg/depcache.h

@@ -1,4 +1,4 @@
-// -*- mode: cpp; mode: fold -*-
+// -*- mode: c++; mode: fold -*-
 // Description								/*{{{*/
 // $Id: depcache.h,v 1.14 2001/02/20 07:03:17 jgg Exp $
 /* ######################################################################
@@ -45,9 +45,71 @@
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/progress.h>
 
+#include <regex.h>
+
+#include <vector>
+
 class pkgDepCache : protected pkgCache::Namespace
 {
    public:
+
+   /** \brief An arbitrary predicate on packages. */
+   class InRootSetFunc
+   {
+   public:
+     virtual bool InRootSet(const pkgCache::PkgIterator &pkg) {return false;};
+     virtual ~InRootSetFunc() {};
+   };
+
+   private:
+   /** \brief Mark a single package and all its unmarked important
+    *  dependencies during mark-and-sweep.
+    *
+    *  Recursively invokes itself to mark all dependencies of the
+    *  package.
+    *
+    *  \param pkg The package to mark.
+    *
+    *  \param ver The version of the package that is to be marked.
+    *
+    *  \param follow_recommends If \b true, recommendations of the
+    *  package will be recursively marked.
+    *
+    *  \param follow_suggests If \b true, suggestions of the package
+    *  will be recursively marked.
+    */
+   void MarkPackage(const pkgCache::PkgIterator &pkg,
+		    const pkgCache::VerIterator &ver,
+		    bool follow_recommends,
+		    bool follow_suggests);
+
+   /** \brief Update the Marked field of all packages.
+    *
+    *  Each package's StateCache::Marked field will be set to \b true
+    *  if and only if it can be reached from the root set.  By
+    *  default, the root set consists of the set of manually installed
+    *  or essential packages, but it can be extended using the
+    *  parameter #rootFunc.
+    *
+    *  \param rootFunc A callback that can be used to add extra
+    *  packages to the root set.
+    *
+    *  \return \b false if an error occured.
+    */
+   bool MarkRequired(InRootSetFunc &rootFunc);
+
+   /** \brief Set the StateCache::Garbage flag on all packages that
+    *  should be removed.
+    *
+    *  Packages that were not marked by the last call to #MarkRequired
+    *  are tested to see whether they are actually garbage.  If so,
+    *  they are marked as such.
+    *
+    *  \return \b false if an error occured.
+    */
+   bool Sweep();
+
+   public:
    
    // These flags are used in DepState
    enum DepFlags {DepNow = (1 << 0),DepInstall = (1 << 1),DepCVer = (1 << 2),
@@ -64,6 +126,83 @@ class pkgDepCache : protected pkgCache::Namespace
    enum VersionTypes {NowVersion, InstallVersion, CandidateVersion};
    enum ModeList {ModeDelete = 0, ModeKeep = 1, ModeInstall = 2};
 
+   /** \brief Represents an active action group.
+    *
+    *  An action group is a group of actions that are currently being
+    *  performed.  While an active group is active, certain routine
+    *  clean-up actions that would normally be performed after every
+    *  cache operation are delayed until the action group is
+    *  completed.  This is necessary primarily to avoid inefficiencies
+    *  when modifying a large number of packages at once.
+    *
+    *  This class represents an active action group.  Creating an
+    *  instance will create an action group; destroying one will
+    *  destroy the corresponding action group.
+    *
+    *  The following operations are suppressed by this class:
+    *
+    *    - Keeping the Marked and Garbage flags up to date.
+    *
+    *  \note This can be used in the future to easily accumulate
+    *  atomic actions for undo or to display "what apt did anyway";
+    *  e.g., change the counter of how many action groups are active
+    *  to a std::set of pointers to them and use those to store
+    *  information about what happened in a group in the group.
+    */
+   class ActionGroup
+   {
+       pkgDepCache &cache;
+
+       bool released;
+
+       /** Action groups are noncopyable. */
+       ActionGroup(const ActionGroup &other);
+   public:
+       /** \brief Create a new ActionGroup.
+	*
+	*  \param cache The cache that this ActionGroup should
+	*  manipulate.
+	*
+	*  As long as this object exists, no automatic cleanup
+	*  operations will be undertaken.
+	*/
+       ActionGroup(pkgDepCache &cache);
+
+       /** \brief Clean up the action group before it is destroyed.
+        *
+        *  If it is destroyed later, no second cleanup wil be run.
+	*/
+       void release();
+
+       /** \brief Destroy the action group.
+	*
+	*  If this is the last action group, the automatic cache
+	*  cleanup operations will be undertaken.
+	*/
+       ~ActionGroup();
+   };
+
+   /** \brief Returns \b true for packages matching a regular
+    *  expression in APT::NeverAutoRemove.
+    */
+   class DefaultRootSetFunc : public InRootSetFunc
+   {
+     std::vector<regex_t *> rootSetRegexp;
+     bool constructedSuccessfully;
+
+   public:
+     DefaultRootSetFunc();
+     ~DefaultRootSetFunc();
+
+     /** \return \b true if the class initialized successfully, \b
+      *  false otherwise.  Used to avoid throwing an exception, since
+      *  APT classes generally don't.
+      */
+     bool wasConstructedSuccessfully() const { return constructedSuccessfully; }
+
+     bool InRootSet(const pkgCache::PkgIterator &pkg);
+   };
+
    struct StateCache
    {
       // Epoch stripped text versions of the two version fields
@@ -80,8 +219,15 @@ class pkgDepCache : protected pkgCache::Namespace
       unsigned short Flags;
       unsigned short iFlags;           // Internal flags
 
-      // mark and sweep flags
+      /** \brief \b true if this package can be reached from the root set. */
       bool Marked;
+
+      /** \brief \b true if this package is unused and should be removed.
+       *
+       *  This differs from !#Marked, because it is possible that some
+       *  unreachable packages will be protected from becoming
+       *  garbage.
+       */
       bool Garbage;
 
       // Various tree indicators
@@ -124,6 +270,14 @@ class pkgDepCache : protected pkgCache::Namespace
       
       virtual ~Policy() {};
    };
+
+   private:
+   /** The number of open "action groups"; certain post-action
+    *  operations are suppressed if this number is > 0.
+    */
+   int group_level;
+
+   friend class ActionGroup;
      
    protected:
 
@@ -187,13 +341,61 @@ class pkgDepCache : protected pkgCache::Namespace
    inline StateCache &operator [](PkgIterator const &I) {return PkgState[I->ID];};
    inline unsigned char &operator [](DepIterator const &I) {return DepState[I->ID];};
 
-   // Manipulators
-   void MarkKeep(PkgIterator const &Pkg,bool Soft = false);
+   /** \return A function identifying packages in the root set other
+    *  than manually installed packages and essential packages, or \b
+    *  NULL if an error occurs.
+    *
+    *  \todo Is this the best place for this function?  Perhaps the
+    *  settings for mark-and-sweep should be stored in a single
+    *  external class?
+    */
+   virtual InRootSetFunc *GetRootSetFunc();
+
+   /** \return \b true if the garbage collector should follow recommendations.
+    */
+   virtual bool MarkFollowsRecommends();
+
+   /** \return \b true if the garbage collector should follow suggestions.
+    */
+   virtual bool MarkFollowsSuggests();
+
+   /** \brief Update the Marked and Garbage fields of all packages.
+    *
+    *  This routine is implicitly invoked after all state manipulators
+    *  and when an ActionGroup is destroyed.  It invokes #MarkRequired
+    *  and #Sweep to do its dirty work.
+    *
+    *  \param rootFunc A predicate that returns \b true for packages
+    *  that should be added to the root set.
+    */
+   bool MarkAndSweep(InRootSetFunc &rootFunc)
+   {
+     return MarkRequired(rootFunc) && Sweep();
+   }
+
+   bool MarkAndSweep()
+   {
+     std::auto_ptr<InRootSetFunc> f(GetRootSetFunc());
+     if(f.get() != NULL)
+       return MarkAndSweep(*f.get());
+     else
+       return false;
+   }
+
+   /** \name State Manipulators
+    */
+   // @{
+   void MarkKeep(PkgIterator const &Pkg, bool Soft = false,
+		 bool FromUser = true);
    void MarkDelete(PkgIterator const &Pkg,bool Purge = false);
    void MarkInstall(PkgIterator const &Pkg,bool AutoInst = true,
-		    unsigned long Depth = 0);
+		    unsigned long Depth = 0, bool FromUser = true);
    void SetReInstall(PkgIterator const &Pkg,bool To);
    void SetCandidateVersion(VerIterator TargetVer);
+
+   /** Set the "is automatically installed" flag of Pkg. */
+   void MarkAuto(const PkgIterator &Pkg, bool Auto);
+   // @}
    
    // This is for debuging
    void Update(OpProgress *Prog = 0);

+ 1 - 1
apt-pkg/packagemanager.cc

@@ -106,7 +106,7 @@ bool pkgPackageManager::FixMissing()
    
       // Okay, this file is missing and we need it. Mark it for keep 
       Bad = true;
-      Cache.MarkKeep(I);
+      Cache.MarkKeep(I, false, false);
    }
  
    // We have to empty the list otherwise it will not have the new changes

+ 23 - 12
cmdline/apt-get.cc

@@ -1370,19 +1370,23 @@ bool DoAutomaticRemove(CacheFile &Cache)
       return _error->Error(_("We are not supposed to delete stuff, can't "
 			     "start AutoRemover"));
 
-   // do the actual work
-   pkgMarkUsed(Cache);
-
-   // look over the cache to see what can be removed
-   for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); ! Pkg.end(); ++Pkg)
    {
-      if (Cache[Pkg].Garbage &&
-          (Pkg->CurrentVer != 0 && Cache[Pkg].Install() == false && 
-	   Cache[Pkg].Delete() == false))
-      {
-         fprintf(stdout,"We could delete %s\n", Pkg.Name());
-         Cache->MarkDelete(Pkg,_config->FindB("APT::Get::Purge",false));
-      }
+     pkgDepCache::ActionGroup group(*Cache);
+
+     // look over the cache to see what can be removed
+     for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); ! Pkg.end(); ++Pkg)
+       {
+	 if (Cache[Pkg].Garbage)
+	   {
+	     if(Pkg.CurrentVer() != 0 || Cache[Pkg].Install())
+	       fprintf(stdout,"We could delete %s\n", Pkg.Name());
+
+	     if(Pkg.CurrentVer() != 0 && Pkg->CurrentState != pkgCache::State::ConfigFiles)
+	       Cache->MarkDelete(Pkg, _config->FindB("APT::Get::Purge", false));
+	     else
+	       Cache->MarkKeep(Pkg, false, false);
+	   }
+       }
    }
 
    // Now see if we destroyed anything
@@ -1399,6 +1403,7 @@ bool DoAutomaticRemove(CacheFile &Cache)
    }
    return true;
 }
+
 // DoUpgrade - Upgrade all packages					/*{{{*/
 // ---------------------------------------------------------------------
 /* Upgrade all packages without installing new packages or erasing old
@@ -1450,6 +1455,11 @@ bool DoInstall(CommandLine &CmdL)
    bool DefRemove = false;
    if (strcasecmp(CmdL.FileList[0],"remove") == 0)
       DefRemove = true;
+   else if (strcasecmp(CmdL.FileList[0], "autoremove") == 0)
+     {
+       _config->Set("APT::Get::AutomaticRemove", "true");
+       DefRemove = true;
+     }
 
    for (const char **I = CmdL.FileList + 1; *I != 0; I++)
    {
@@ -2533,6 +2543,7 @@ int main(int argc,const char *argv[])
                                    {"upgrade",&DoUpgrade},
                                    {"install",&DoInstall},
                                    {"remove",&DoInstall},
+				   {"autoremove",&DoInstall},
                                    {"dist-upgrade",&DoDistUpgrade},
                                    {"dselect-upgrade",&DoDSelectUpgrade},
 				   {"build-dep",&DoBuildDep},

Разница между файлами не показана из-за своего большого размера
+ 358 - 218
po/apt-all.pot