releasing package apt version 1.0.5
[apt.git] / apt-pkg / orderlist.cc
index 0ee2e2bc8bfccea7de8e1a5e645020c8ba663e4b..a1fcbcc98cd41d63f1247ca1faba934e625930ff 100644 (file)
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
+#include<config.h>
+
 #include <apt-pkg/orderlist.h>
 #include <apt-pkg/depcache.h>
 #include <apt-pkg/error.h>
-#include <apt-pkg/version.h>
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/configuration.h>
+#include <apt-pkg/cacheiterators.h>
+#include <apt-pkg/pkgcache.h>
 
+#include <stdlib.h>
+#include <string.h>
+#include <string>
 #include <iostream>
                                                                        /*}}}*/
 
@@ -80,16 +86,14 @@ pkgOrderList *pkgOrderList::Me = 0;
 // OrderList::pkgOrderList - Constructor                               /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-pkgOrderList::pkgOrderList(pkgDepCache *pCache) : Cache(*pCache)
+pkgOrderList::pkgOrderList(pkgDepCache *pCache) : Cache(*pCache),
+                                                 Primary(NULL), Secondary(NULL),
+                                                 RevDepends(NULL), Remove(NULL),
+                                                 AfterEnd(NULL), FileList(NULL),
+                                                 LoopCount(-1), Depth(0)
 {
-   FileList = 0;
-   Primary = 0;
-   Secondary = 0;
-   RevDepends = 0;
-   Remove = 0;
-   LoopCount = -1;
    Debug = _config->FindB("Debug::pkgOrderList",false);
-   
+
    /* Construct the arrays, egcs 1.0.1 bug requires the package count
       hack */
    unsigned long Size = Cache.Head().PackageCount;
@@ -117,7 +121,8 @@ bool pkgOrderList::IsMissing(PkgIterator Pkg)
       return false;
 
    // Skip Packages that need configure only.
-   if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure && 
+   if ((Pkg.State() == pkgCache::PkgIterator::NeedsConfigure ||
+        Pkg.State() == pkgCache::PkgIterator::NeedsNothing) &&
        Cache[Pkg].Keep() == true)
       return false;
 
@@ -126,6 +131,7 @@ bool pkgOrderList::IsMissing(PkgIterator Pkg)
    
    if (FileList[Pkg->ID].empty() == false)
       return false;
+
    return true;
 }
                                                                        /*}}}*/
@@ -143,14 +149,14 @@ bool pkgOrderList::DoRun()
    Depth = 0;
    WipeFlags(Added | AddPending | Loop | InList);
 
-   for (iterator I = List; I != End; I++)
+   for (iterator I = List; I != End; ++I)
       Flag(*I,InList);
 
    // Rebuild the main list into the temp list.
    iterator OldEnd = End;
    End = NList;
-   for (iterator I = List; I != OldEnd; I++)
-      if (VisitNode(PkgIterator(Cache,*I)) == false)
+   for (iterator I = List; I != OldEnd; ++I)
+      if (VisitNode(PkgIterator(Cache,*I), "DoRun") == false)
       {
         End = OldEnd;
         return false;
@@ -195,11 +201,11 @@ bool pkgOrderList::OrderCritical()
    {
       clog << "** Critical Unpack ordering done" << endl;
 
-      for (iterator I = List; I != End; I++)
+      for (iterator I = List; I != End; ++I)
       {
         PkgIterator P(Cache,*I);
         if (IsNow(P) == true)
-           clog << "  " << P.Name() << ' ' << IsMissing(P) << ',' << IsFlag(P,After) << endl;
+           clog << "  " << P.FullName() << ' ' << IsMissing(P) << ',' << IsFlag(P,After) << endl;
       }
    }
 
@@ -220,7 +226,7 @@ bool pkgOrderList::OrderUnpack(string *FileList)
       WipeFlags(After);
 
       // Set the inlist flag
-      for (iterator I = List; I != End; I++)
+      for (iterator I = List; I != End; ++I)
       {
         PkgIterator P(Cache,*I);
         if (IsMissing(P) == true && IsNow(P) == true)
@@ -268,11 +274,11 @@ bool pkgOrderList::OrderUnpack(string *FileList)
    {
       clog << "** Unpack ordering done" << endl;
 
-      for (iterator I = List; I != End; I++)
+      for (iterator I = List; I != End; ++I)
       {
         PkgIterator P(Cache,*I);
         if (IsNow(P) == true)
-           clog << "  " << P.Name() << ' ' << IsMissing(P) << ',' << IsFlag(P,After) << endl;
+           clog << "  " << P.FullName() << ' ' << IsMissing(P) << ',' << IsFlag(P,After) << endl;
       }
    }
 
@@ -299,9 +305,8 @@ bool pkgOrderList::OrderConfigure()
 /* Higher scores order earlier */
 int pkgOrderList::Score(PkgIterator Pkg)
 {
-   static int const ScoreDelete = _config->FindI("OrderList::Score::Delete", 500);
-
-   // Removal is always done first
+   // Removals should be done after we dealt with essentials
+   static int const ScoreDelete = _config->FindI("OrderList::Score::Delete", 100);
    if (Cache[Pkg].Delete() == true)
       return ScoreDelete;
 
@@ -321,7 +326,7 @@ int pkgOrderList::Score(PkgIterator Pkg)
       Score += ScoreImmediate;
 
    for (DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList();
-       D.end() == false; D++)
+       D.end() == false; ++D)
       if (D->Type == pkgCache::Dep::PreDepends)
       {
         Score += ScorePreDepends;
@@ -329,9 +334,11 @@ int pkgOrderList::Score(PkgIterator Pkg)
       }
 
    // Important Required Standard Optional Extra
-   signed short PrioMap[] = {0,5,4,3,1,0};
    if (Cache[Pkg].InstVerIter(Cache)->Priority <= 5)
+   {
+      signed short PrioMap[] = {0,5,4,3,1,0};
       Score += PrioMap[Cache[Pkg].InstVerIter(Cache)->Priority];
+   }
    return Score;
 }
                                                                        /*}}}*/
@@ -486,44 +493,76 @@ bool pkgOrderList::VisitRProvides(DepFunc F,VerIterator Ver)
       return true;
    
    bool Res = true;
-   for (PrvIterator P = Ver.ProvidesList(); P.end() == false; P++)
+   for (PrvIterator P = Ver.ProvidesList(); P.end() == false; ++P)
       Res &= (this->*F)(P.ParentPkg().RevDependsList());
-   return true;
+   return Res;
 }
                                                                        /*}}}*/
 // OrderList::VisitProvides - Visit all of the providing packages      /*{{{*/
 // ---------------------------------------------------------------------
-/* This routine calls visit on all providing packages. */
+/* This routine calls visit on all providing packages.
+
+   If the dependency is negative it first visits packages which are
+   intended to be removed and after that all other packages.
+   It does so to avoid situations in which this package is used to
+   satisfy a (or-group/provides) dependency of another package which
+   could have been satisfied also by upgrading another package -
+   otherwise we have more broken packages dpkg needs to auto-
+   deconfigure and in very complicated situations it even decides
+   against it! */
 bool pkgOrderList::VisitProvides(DepIterator D,bool Critical)
-{   
+{
    SPtrArray<Version *> List = D.AllTargets();
-   for (Version **I = List; *I != 0; I++)
+   for (Version **I = List; *I != 0; ++I)
    {
       VerIterator Ver(Cache,*I);
       PkgIterator Pkg = Ver.ParentPkg();
 
+      if (D.IsNegative() == true && Cache[Pkg].Delete() == false)
+        continue;
+
       if (Cache[Pkg].Keep() == true && Pkg.State() == PkgIterator::NeedsNothing)
         continue;
-      
-      if (D->Type != pkgCache::Dep::Conflicts &&
-         D->Type != pkgCache::Dep::DpkgBreaks &&
-         D->Type != pkgCache::Dep::Obsoletes &&
+
+      if (D.IsNegative() == false &&
          Cache[Pkg].InstallVer != *I)
         continue;
-      
-      if ((D->Type == pkgCache::Dep::Conflicts ||
-          D->Type == pkgCache::Dep::DpkgBreaks ||
-          D->Type == pkgCache::Dep::Obsoletes) &&
+
+      if (D.IsNegative() == true &&
          (Version *)Pkg.CurrentVer() != *I)
         continue;
-      
+
       // Skip over missing files
       if (Critical == false && IsMissing(D.ParentPkg()) == true)
         continue;
 
-      if (VisitNode(Pkg) == false)
+      if (VisitNode(Pkg, "Provides-1") == false)
         return false;
    }
+   if (D.IsNegative() == false)
+      return true;
+   for (Version **I = List; *I != 0; ++I)
+   {
+      VerIterator Ver(Cache,*I);
+      PkgIterator Pkg = Ver.ParentPkg();
+
+      if (Cache[Pkg].Delete() == true)
+        continue;
+
+      if (Cache[Pkg].Keep() == true && Pkg.State() == PkgIterator::NeedsNothing)
+        continue;
+
+      if ((Version *)Pkg.CurrentVer() != *I)
+        continue;
+
+      // Skip over missing files
+      if (Critical == false && IsMissing(D.ParentPkg()) == true)
+        continue;
+
+      if (VisitNode(Pkg, "Provides-2") == false)
+        return false;
+   }
+
    return true;
 }
                                                                        /*}}}*/
@@ -531,10 +570,10 @@ bool pkgOrderList::VisitProvides(DepIterator D,bool Critical)
 // ---------------------------------------------------------------------
 /* This is the core ordering routine. It calls the set dependency
    consideration functions which then potentialy call this again. Finite
-   depth is achived through the colouring mechinism. */
-bool pkgOrderList::VisitNode(PkgIterator Pkg)
+   depth is achieved through the colouring mechinism. */
+bool pkgOrderList::VisitNode(PkgIterator Pkg, char const* from)
 {
-   // Looping or irrelevent.
+   // Looping or irrelevant.
    // This should probably trancend not installed packages
    if (Pkg.end() == true || IsFlag(Pkg,Added) == true || 
        IsFlag(Pkg,AddPending) == true || IsFlag(Pkg,InList) == false)
@@ -543,7 +582,7 @@ bool pkgOrderList::VisitNode(PkgIterator Pkg)
    if (Debug == true)
    {
       for (int j = 0; j != Depth; j++) clog << ' ';
-      clog << "Visit " << Pkg.Name() << endl;
+      clog << "Visit " << Pkg.FullName() << " from " << from << endl;
    }
    
    Depth++;
@@ -602,7 +641,7 @@ bool pkgOrderList::VisitNode(PkgIterator Pkg)
    if (Debug == true)
    {
       for (int j = 0; j != Depth; j++) clog << ' ';
-      clog << "Leave " << Pkg.Name() << ' ' << IsFlag(Pkg,Added) << ',' << IsFlag(Pkg,AddPending) << endl;
+      clog << "Leave " << Pkg.FullName() << ' ' << IsFlag(Pkg,Added) << ',' << IsFlag(Pkg,AddPending) << endl;
    }
    
    return true;
@@ -617,7 +656,7 @@ bool pkgOrderList::VisitNode(PkgIterator Pkg)
    Loops are preprocessed and logged. */
 bool pkgOrderList::DepUnPackCrit(DepIterator D)
 {
-   for (; D.end() == false; D++)
+   for (; D.end() == false; ++D)
    {
       if (D.Reverse() == true)
       {
@@ -638,16 +677,14 @@ bool pkgOrderList::DepUnPackCrit(DepIterator D)
         if (CheckDep(D) == true)
            continue;
 
-        if (VisitNode(D.ParentPkg()) == false)
+        if (VisitNode(D.ParentPkg(), "UnPackCrit") == false)
            return false;
       }
       else
       {
         /* Forward critical dependencies MUST be correct before the 
            package can be unpacked. */
-        if (D->Type != pkgCache::Dep::Conflicts &&
-            D->Type != pkgCache::Dep::DpkgBreaks &&
-            D->Type != pkgCache::Dep::Obsoletes &&
+        if (D.IsNegative() == false &&
             D->Type != pkgCache::Dep::PreDepends)
            continue;
                                 
@@ -697,7 +734,7 @@ bool pkgOrderList::DepUnPackPreD(DepIterator D)
    if (D.Reverse() == true)
       return DepUnPackCrit(D);
    
-   for (; D.end() == false; D++)
+   for (; D.end() == false; ++D)
    {
       if (D.IsCritical() == false)
         continue;
@@ -740,7 +777,7 @@ bool pkgOrderList::DepUnPackPre(DepIterator D)
    if (D.Reverse() == true)
       return true;
    
-   for (; D.end() == false; D++)
+   for (; D.end() == false; ++D)
    {
       /* Only consider the PreDepends or Depends. Depends are only
                 considered at the lowest depth or in the case of immediate
@@ -791,11 +828,11 @@ bool pkgOrderList::DepUnPackPre(DepIterator D)
    The forwards depends loop is designed to bring the packages dependents
    close to the package. This helps reduce deconfigure time. 
    
-   Loops are irrelevent to this. */
+   Loops are irrelevant to this. */
 bool pkgOrderList::DepUnPackDep(DepIterator D)
 {
    
-   for (; D.end() == false; D++)
+   for (; D.end() == false; ++D)
       if (D.IsCritical() == true)
       {
         if (D.Reverse() == true)
@@ -807,7 +844,7 @@ bool pkgOrderList::DepUnPackDep(DepIterator D)
                D.ParentPkg().CurrentVer() != D.ParentVer())
               continue;
 
-           // The dep will not break so it is irrelevent.
+           // The dep will not break so it is irrelevant.
            if (CheckDep(D) == true)
               continue;
            
@@ -815,7 +852,7 @@ bool pkgOrderList::DepUnPackDep(DepIterator D)
            if (IsMissing(D.ParentPkg()) == true)
               continue;
            
-           if (VisitNode(D.ParentPkg()) == false)
+           if (VisitNode(D.ParentPkg(), "UnPackDep-Parent") == false)
               return false;
         }
         else
@@ -829,7 +866,7 @@ bool pkgOrderList::DepUnPackDep(DepIterator D)
               if (CheckDep(D) == true)
                 continue;
 
-              if (VisitNode(D.TargetPkg()) == false)
+              if (VisitNode(D.TargetPkg(), "UnPackDep-Target") == false)
                 return false;
            }
         }
@@ -850,7 +887,7 @@ bool pkgOrderList::DepConfigure(DepIterator D)
    if (D.Reverse() == true)
       return true;
    
-   for (; D.end() == false; D++)
+   for (; D.end() == false; ++D)
       if (D->Type == pkgCache::Dep::Depends)
         if (VisitProvides(D,false) == false)
            return false;
@@ -859,52 +896,136 @@ bool pkgOrderList::DepConfigure(DepIterator D)
                                                                        /*}}}*/
 // OrderList::DepRemove - Removal ordering                             /*{{{*/
 // ---------------------------------------------------------------------
-/* Removal visits all reverse depends. It considers if the dependency
-   of the Now state version to see if it is okay with removing this
-   package. This check should always fail, but is provided for symetery
-   with the other critical handlers.
-   Loops are preprocessed and logged. Removal loops can also be
-   detected in the critical handler. They are characterized by an
-   old version of A depending on B but the new version of A conflicting
-   with B, thus either A or B must break to install. */
-bool pkgOrderList::DepRemove(DepIterator D)
+/* Checks all given dependencies if they are broken by the remove of a
+   package and if so fix it by visiting another provider or or-group
+   member to ensure that the dependee keeps working which is especially
+   important for Immediate packages like e.g. those depending on an
+   awk implementation. If the dependency can't be fixed with another
+   package this means an upgrade of the package will solve the problem. */
+bool pkgOrderList::DepRemove(DepIterator Broken)
 {
-   if (D.Reverse() == false)
+   if (Broken.Reverse() == false)
       return true;
-   for (; D.end() == false; D++)
-      if (D->Type == pkgCache::Dep::Depends || D->Type == pkgCache::Dep::PreDepends)
+
+   for (; Broken.end() == false; ++Broken)
+   {
+      if (Broken->Type != pkgCache::Dep::Depends &&
+           Broken->Type != pkgCache::Dep::PreDepends)
+        continue;
+
+      PkgIterator BrokenPkg = Broken.ParentPkg();
+      // uninstalled packages can't break via a remove
+      if (BrokenPkg->CurrentVer == 0)
+        continue;
+
+      // if its already added, we can't do anything useful
+      if (IsFlag(BrokenPkg, AddPending) == true || IsFlag(BrokenPkg, Added) == true)
+        continue;
+
+      // if the dependee is going to be removed, visit it now
+      if (Cache[BrokenPkg].Delete() == true)
+        return VisitNode(BrokenPkg, "Remove-Dependee");
+
+      // The package stays around, so find out how this is possible
+      for (DepIterator D = BrokenPkg.CurrentVer().DependsList(); D.end() == false;)
       {
-        // Duplication elimination, consider the current version only
-        if (D.ParentPkg().CurrentVer() != D.ParentVer())
+        // only important or-groups need fixing
+        if (D->Type != pkgCache::Dep::Depends &&
+              D->Type != pkgCache::Dep::PreDepends)
+        {
+           ++D;
            continue;
+        }
 
-        /* We wish to see if the dep on the parent package is okay
-           in the removed (install) state of the target pkg. */         
-        if (CheckDep(D) == true)
+        // Start is the beginning of the or-group, D is the first one after or
+        DepIterator Start = D;
+        bool foundBroken = false;
+        for (bool LastOR = true; D.end() == false && LastOR == true; ++D)
         {
-           // We want to catch loops with the code below.
-           if (IsFlag(D.ParentPkg(),AddPending) == false)
-              continue;
+           LastOR = (D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or;
+           if (D == Broken)
+              foundBroken = true;
         }
 
-        // This is the loop detection
-        if (IsFlag(D.ParentPkg(),Added) == true || 
-            IsFlag(D.ParentPkg(),AddPending) == true)
-        {
-           if (IsFlag(D.ParentPkg(),AddPending) == true)
-              AddLoop(D);
+        // this or-group isn't the broken one: keep searching
+        if (foundBroken == false)
            continue;
+
+        // iterate over all members of the or-group searching for a ready replacement
+        bool readyReplacement = false;
+        for (DepIterator OrMember = Start; OrMember != D && readyReplacement == false; ++OrMember)
+        {
+           Version ** Replacements = OrMember.AllTargets();
+           for (Version **R = Replacements; *R != 0; ++R)
+           {
+              VerIterator Ver(Cache,*R);
+              // only currently installed packages can be a replacement
+              PkgIterator RPkg = Ver.ParentPkg();
+              if (RPkg.CurrentVer() != Ver)
+                 continue;
+
+              // packages going to be removed can't be a replacement
+              if (Cache[RPkg].Delete() == true)
+                 continue;
+
+              readyReplacement = true;
+              break;
+           }
+           delete[] Replacements;
         }
 
-        // Skip over missing files
-        if (IsMissing(D.ParentPkg()) == true)
+        // something else is ready to take over, do nothing
+        if (readyReplacement == true)
            continue;
-        
-        if (VisitNode(D.ParentPkg()) == false)
+
+        // see if we can visit a replacement
+        bool visitReplacement = false;
+        for (DepIterator OrMember = Start; OrMember != D && visitReplacement == false; ++OrMember)
+        {
+           Version ** Replacements = OrMember.AllTargets();
+           for (Version **R = Replacements; *R != 0; ++R)
+           {
+              VerIterator Ver(Cache,*R);
+              // consider only versions we plan to install
+              PkgIterator RPkg = Ver.ParentPkg();
+              if (Cache[RPkg].Install() == false || Cache[RPkg].InstallVer != Ver)
+                 continue;
+
+              // loops are not going to help us, so don't create them
+              if (IsFlag(RPkg, AddPending) == true)
+                 continue;
+
+              if (IsMissing(RPkg) == true)
+                 continue;
+
+              visitReplacement = true;
+              if (IsFlag(BrokenPkg, Immediate) == false)
+              {
+                 if (VisitNode(RPkg, "Remove-Rep") == true)
+                    break;
+              }
+              else
+              {
+                 Flag(RPkg, Immediate);
+                 if (VisitNode(RPkg, "Remove-ImmRep") == true)
+                    break;
+              }
+              visitReplacement = false;
+           }
+           delete[] Replacements;
+        }
+        if (visitReplacement == true)
+           continue;
+
+        // the broken package in current version can't be fixed, so install new version
+        if (IsMissing(BrokenPkg) == true)
+           break;
+
+        if (VisitNode(BrokenPkg, "Remove-Upgrade") == false)
            return false;
       }
-   
+   }
+
    return true;
 }
                                                                        /*}}}*/
@@ -928,8 +1049,10 @@ bool pkgOrderList::AddLoop(DepIterator D)
    Loops[LoopCount++] = D;
    
    // Mark the packages as being part of a loop.
-   Flag(D.TargetPkg(),Loop);
-   Flag(D.ParentPkg(),Loop);
+   //Flag(D.TargetPkg(),Loop);
+   //Flag(D.ParentPkg(),Loop);
+   /* This is currently disabled because the Loop flag is being used for
+      loop management in the package manager. Check the orderlist.h file for more info */
    return true;
 }
                                                                        /*}}}*/
@@ -978,10 +1101,14 @@ bool pkgOrderList::CheckDep(DepIterator D)
       
       /* Conflicts requires that all versions are not present, depends
          just needs one */
-      if (D->Type != pkgCache::Dep::Conflicts && 
-         D->Type != pkgCache::Dep::DpkgBreaks && 
-         D->Type != pkgCache::Dep::Obsoletes)
+      if (D.IsNegative() == false)
       {
+        // ignore provides by older versions of this package
+        if (((D.Reverse() == false && Pkg == D.ParentPkg()) ||
+             (D.Reverse() == true && Pkg == D.TargetPkg())) &&
+            Cache[Pkg].InstallVer != *I)
+           continue;
+
         /* Try to find something that does not have the after flag set
            if at all possible */
         if (IsFlag(Pkg,After) == true)