Fix incorrect upgradable listing in "apt list" (thanks to Michael Musenbrock)
[ntk/apt.git] / apt-private / private-output.cc
index 420ca14..2120b7a 100644 (file)
@@ -7,15 +7,23 @@
 #include <apt-pkg/cachefile.h>
 #include <apt-pkg/pkgrecords.h>
 #include <apt-pkg/policy.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/cacheiterators.h>
 
+#include <apt-private/private-output.h>
+#include <apt-private/private-cachefile.h>
+
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <iomanip>
 #include <iostream>
-#include <locale.h>
 #include <langinfo.h>
 #include <unistd.h>
-
-#include "private-output.h"
-#include "private-cachefile.h"
+#include <signal.h>
+#include <sys/ioctl.h>
 
 #include <apti18n.h>
                                                                        /*}}}*/
@@ -26,8 +34,24 @@ std::ostream c0out(0);
 std::ostream c1out(0);
 std::ostream c2out(0);
 std::ofstream devnull("/dev/null");
+
+
 unsigned int ScreenWidth = 80 - 1; /* - 1 for the cursor */
 
+// SigWinch - Window size change signal handler                                /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static void SigWinch(int)
+{
+   // Riped from GNU ls
+#ifdef TIOCGWINSZ
+   struct winsize ws;
+  
+   if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col >= 5)
+      ScreenWidth = ws.ws_col - 1;
+#endif
+}
+                                                                       /*}}}*/
 bool InitOutput()                                                      /*{{{*/
 {
    if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
@@ -41,6 +65,10 @@ bool InitOutput()                                                    /*{{{*/
    if (_config->FindI("quiet",0) > 1)
       c1out.rdbuf(devnull.rdbuf());
 
+   // deal with window size changes
+   signal(SIGWINCH,SigWinch);
+   SigWinch(0);
+
    if(!isatty(1))
    {
       _config->Set("APT::Color", "false");
@@ -63,10 +91,10 @@ bool InitOutput()                                                   /*{{{*/
    return true;
 }
                                                                        /*}}}*/
-std::string GetArchiveSuite(pkgCacheFile &CacheFile, pkgCache::VerIterator ver) /*{{{*/
+static std::string GetArchiveSuite(pkgCacheFile &/*CacheFile*/, pkgCache::VerIterator ver) /*{{{*/
 {
    std::string suite = "";
-   if (ver && ver.FileList() && ver.FileList())
+   if (ver && ver.FileList())
    {
       pkgCache::VerFileIterator VF = ver.FileList();
       for (; VF.end() == false ; ++VF)
@@ -82,7 +110,7 @@ std::string GetArchiveSuite(pkgCacheFile &CacheFile, pkgCache::VerIterator ver)
    return suite;
 }
                                                                        /*}}}*/
-std::string GetFlagsStr(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+static std::string GetFlagsStr(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
 {
    pkgDepCache *DepCache = CacheFile.GetDepCache();
    pkgDepCache::StateCache &state = (*DepCache)[P];
@@ -90,7 +118,7 @@ std::string GetFlagsStr(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
    std::string flags_str;
    if (state.NowBroken())
       flags_str = "B";
-   if (P.CurrentVer() && state.Upgradable())
+   if (P.CurrentVer() && state.Upgradable() && state.CandidateVer != NULL)
       flags_str = "g";
    else if (P.CurrentVer() != NULL)
       flags_str = "i";
@@ -99,7 +127,7 @@ std::string GetFlagsStr(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
    return flags_str;
 }
                                                                        /*}}}*/
-std::string GetCandidateVersion(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+static std::string GetCandidateVersion(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
 {
    pkgPolicy *policy = CacheFile.GetPolicy();
    pkgCache::VerIterator cand = policy->GetCandidateVer(P);
@@ -107,14 +135,14 @@ std::string GetCandidateVersion(pkgCacheFile &CacheFile, pkgCache::PkgIterator P
    return cand ? cand.VerStr() : "(none)";
 }
                                                                        /*}}}*/
-std::string GetInstalledVersion(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+static std::string GetInstalledVersion(pkgCacheFile &/*CacheFile*/, pkgCache::PkgIterator P)/*{{{*/
 {
    pkgCache::VerIterator inst = P.CurrentVer();
 
    return inst ? inst.VerStr() : "(none)";
 }
                                                                        /*}}}*/
-std::string GetVersion(pkgCacheFile &CacheFile, pkgCache::VerIterator V)/*{{{*/
+static std::string GetVersion(pkgCacheFile &/*CacheFile*/, pkgCache::VerIterator V)/*{{{*/
 {
    pkgCache::PkgIterator P = V.ParentPkg();
    if (V == P.CurrentVer())
@@ -134,16 +162,20 @@ std::string GetVersion(pkgCacheFile &CacheFile, pkgCache::VerIterator V)/*{{{*/
    return "(none)";
 }
                                                                        /*}}}*/
-std::string GetArchitecture(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+static std::string GetArchitecture(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
 {
    pkgPolicy *policy = CacheFile.GetPolicy();
    pkgCache::VerIterator inst = P.CurrentVer();
    pkgCache::VerIterator cand = policy->GetCandidateVer(P);
-   
+
+   // this may happen for packages in dpkg "deinstall ok config-file" state
+   if (inst.IsGood() == false && cand.IsGood() == false)
+      return P.VersionList().Arch();
+
    return inst ? inst.Arch() : cand.Arch();
 }
                                                                        /*}}}*/
-std::string GetShortDescription(pkgCacheFile &CacheFile, pkgRecords &records, pkgCache::PkgIterator P)/*{{{*/
+static std::string GetShortDescription(pkgCacheFile &CacheFile, pkgRecords &records, pkgCache::PkgIterator P)/*{{{*/
 {
    pkgPolicy *policy = CacheFile.GetPolicy();
 
@@ -188,7 +220,7 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,        /*{{{*/
       output = SubstVar(output, "${Version}", GetVersion(CacheFile, V));
       output = SubstVar(output, "${Description}", GetShortDescription(CacheFile, records, P));
       output = SubstVar(output, "${Origin}", GetArchiveSuite(CacheFile, V));
-      out << output << std::endl;
+      out << output;
    } else {
       // raring/linux-kernel version [upradable: new-version]
       //    description
@@ -197,7 +229,11 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,       /*{{{*/
       std::string CandidateVerStr = GetCandidateVersion(CacheFile, P);
       std::string InstalledVerStr = GetInstalledVersion(CacheFile, P);
       std::string StatusStr;
-      if(P.CurrentVer() == V && state.Upgradable()) {
+      if(P.CurrentVer() == V && 
+         state.Upgradable() &&
+         state.CandidateVer != NULL &&
+         policy->GetCandidateVer(P) != P.CurrentVer())
+      {
          strprintf(StatusStr, _("[installed,upgradable to: %s]"),
                    CandidateVerStr.c_str());
       } else if (P.CurrentVer() == V) {
@@ -312,129 +348,141 @@ bool ShowList(ostream &out,string Title,string List,string VersionsList)
            Depends: libldap2 (>= 2.0.2-2) but it is not going to be installed
            Depends: libsasl7 but it is not going to be installed   
  */
-void ShowBroken(ostream &out,CacheFile &Cache,bool Now)
+static void ShowBrokenPackage(ostream &out, pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg, bool const Now)
 {
-   if (Cache->BrokenCount() == 0)
+   if (Now == true)
+   {
+      if ((*Cache)[Pkg].NowBroken() == false)
+        return;
+   }
+   else
+   {
+      if ((*Cache)[Pkg].InstBroken() == false)
+        return;
+   }
+
+   // Print out each package and the failed dependencies
+   out << " " << Pkg.FullName(true) << " :";
+   unsigned const Indent = Pkg.FullName(true).size() + 3;
+   bool First = true;
+   pkgCache::VerIterator Ver;
+
+   if (Now == true)
+      Ver = Pkg.CurrentVer();
+   else
+      Ver = (*Cache)[Pkg].InstVerIter(*Cache);
+
+   if (Ver.end() == true)
+   {
+      out << endl;
       return;
+   }
 
-   out << _("The following packages have unmet dependencies:") << endl;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+   for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false;)
    {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      
+      // Compute a single dependency element (glob or)
+      pkgCache::DepIterator Start;
+      pkgCache::DepIterator End;
+      D.GlobOr(Start,End); // advances D
+
+      if ((*Cache)->IsImportantDep(End) == false)
+        continue;
+
       if (Now == true)
       {
-        if (Cache[I].NowBroken() == false)
+        if (((*Cache)[End] & pkgDepCache::DepGNow) == pkgDepCache::DepGNow)
            continue;
       }
       else
       {
-        if (Cache[I].InstBroken() == false)
+        if (((*Cache)[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall)
            continue;
       }
-      
-      // Print out each package and the failed dependencies
-      out << " " << I.FullName(true) << " :";
-      unsigned const Indent = I.FullName(true).size() + 3;
-      bool First = true;
-      pkgCache::VerIterator Ver;
-      
-      if (Now == true)
-        Ver = I.CurrentVer();
-      else
-        Ver = Cache[I].InstVerIter(Cache);
-      
-      if (Ver.end() == true)
-      {
-        out << endl;
-        continue;
-      }
-      
-      for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false;)
+
+      bool FirstOr = true;
+      while (1)
       {
-        // Compute a single dependency element (glob or)
-        pkgCache::DepIterator Start;
-        pkgCache::DepIterator End;
-        D.GlobOr(Start,End); // advances D
+        if (First == false)
+           for (unsigned J = 0; J != Indent; J++)
+              out << ' ';
+        First = false;
 
-        if (Cache->IsImportantDep(End) == false)
-           continue;
-        
-        if (Now == true)
+        if (FirstOr == false)
         {
-           if ((Cache[End] & pkgDepCache::DepGNow) == pkgDepCache::DepGNow)
-              continue;
+           for (unsigned J = 0; J != strlen(End.DepType()) + 3; J++)
+              out << ' ';
         }
         else
+           out << ' ' << End.DepType() << ": ";
+        FirstOr = false;
+
+        out << Start.TargetPkg().FullName(true);
+
+        // Show a quick summary of the version requirements
+        if (Start.TargetVer() != 0)
+           out << " (" << Start.CompType() << " " << Start.TargetVer() << ")";
+
+        /* Show a summary of the target package if possible. In the case
+           of virtual packages we show nothing */
+        pkgCache::PkgIterator Targ = Start.TargetPkg();
+        if (Targ->ProvidesList == 0)
         {
-           if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall)
-              continue;
-        }
-        
-        bool FirstOr = true;
-        while (1)
-        {
-           if (First == false)
-              for (unsigned J = 0; J != Indent; J++)
-                 out << ' ';
-           First = false;
+           out << ' ';
+           pkgCache::VerIterator Ver = (*Cache)[Targ].InstVerIter(*Cache);
+           if (Now == true)
+              Ver = Targ.CurrentVer();
 
-           if (FirstOr == false)
+           if (Ver.end() == false)
            {
-              for (unsigned J = 0; J != strlen(End.DepType()) + 3; J++)
-                 out << ' ';
+              if (Now == true)
+                 ioprintf(out,_("but %s is installed"),Ver.VerStr());
+              else
+                 ioprintf(out,_("but %s is to be installed"),Ver.VerStr());
            }
            else
-              out << ' ' << End.DepType() << ": ";
-           FirstOr = false;
-           
-           out << Start.TargetPkg().FullName(true);
-        
-           // Show a quick summary of the version requirements
-           if (Start.TargetVer() != 0)
-              out << " (" << Start.CompType() << " " << Start.TargetVer() << ")";
-           
-           /* Show a summary of the target package if possible. In the case
-              of virtual packages we show nothing */    
-           pkgCache::PkgIterator Targ = Start.TargetPkg();
-           if (Targ->ProvidesList == 0)
            {
-              out << ' ';
-              pkgCache::VerIterator Ver = Cache[Targ].InstVerIter(Cache);
-              if (Now == true)
-                 Ver = Targ.CurrentVer();
-                   
-              if (Ver.end() == false)
+              if ((*Cache)[Targ].CandidateVerIter(*Cache).end() == true)
               {
-                 if (Now == true)
-                    ioprintf(out,_("but %s is installed"),Ver.VerStr());
+                 if (Targ->ProvidesList == 0)
+                    out << _("but it is not installable");
                  else
-                    ioprintf(out,_("but %s is to be installed"),Ver.VerStr());
-              }               
+                    out << _("but it is a virtual package");
+              }
               else
-              {
-                 if (Cache[Targ].CandidateVerIter(Cache).end() == true)
-                 {
-                    if (Targ->ProvidesList == 0)
-                       out << _("but it is not installable");
-                    else
-                       out << _("but it is a virtual package");
-                 }               
-                 else
-                    out << (Now?_("but it is not installed"):_("but it is not going to be installed"));
-              }               
+                 out << (Now?_("but it is not installed"):_("but it is not going to be installed"));
            }
-           
-           if (Start != End)
-              out << _(" or");
-           out << endl;
-           
-           if (Start == End)
-              break;
-           ++Start;
-        }       
-      }            
-   }   
+        }
+
+        if (Start != End)
+           out << _(" or");
+        out << endl;
+
+        if (Start == End)
+           break;
+        ++Start;
+      }
+   }
+}
+void ShowBroken(ostream &out, CacheFile &Cache, bool const Now)
+{
+   if (Cache->BrokenCount() == 0)
+      return;
+
+   out << _("The following packages have unmet dependencies:") << endl;
+   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+   {
+      pkgCache::PkgIterator const I(Cache,Cache.List[J]);
+      ShowBrokenPackage(out, &Cache, I, Now);
+   }
+}
+void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now)
+{
+   if (Cache->BrokenCount() == 0)
+      return;
+
+   out << _("The following packages have unmet dependencies:") << endl;
+   for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+      ShowBrokenPackage(out, &Cache, Pkg, Now);
 }
                                                                        /*}}}*/
 // ShowNew - Show packages to newly install                            /*{{{*/