]> git.saurik.com Git - apt.git/blobdiff - apt-private/private-output.cc
CMake: Add unit tests
[apt.git] / apt-private / private-output.cc
index fc76a05bc7a35f93ff4a45057adf2bb176997157..301aa5519aa2fec567d867bd35eaf763eb04c4a4 100644 (file)
@@ -25,6 +25,8 @@
 #include <signal.h>
 #include <sys/ioctl.h>
 
+#include <sstream>
+
 #include <apti18n.h>
                                                                        /*}}}*/
 
@@ -52,14 +54,14 @@ static void SigWinch(int)
 #endif
 }
                                                                        /*}}}*/
-bool InitOutput()                                                      /*{{{*/
+bool InitOutput(std::basic_streambuf<char> * const out)                        /*{{{*/
 {
    if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
       _config->Set("quiet","1");
 
-   c0out.rdbuf(cout.rdbuf());
-   c1out.rdbuf(cout.rdbuf());
-   c2out.rdbuf(cout.rdbuf());
+   c0out.rdbuf(out);
+   c1out.rdbuf(out);
+   c2out.rdbuf(out);
    if (_config->FindI("quiet",0) > 0)
       c0out.rdbuf(devnull.rdbuf());
    if (_config->FindI("quiet",0) > 1)
@@ -199,10 +201,12 @@ static std::string GetShortDescription(pkgCacheFile &CacheFile, pkgRecords &reco
    std::string ShortDescription = "(none)";
    if(ver)
    {
-      pkgCache::DescIterator Desc = ver.TranslatedDescription();
-      pkgRecords::Parser & parser = records.Lookup(Desc.FileList());
-
-      ShortDescription = parser.ShortDesc();
+      pkgCache::DescIterator const Desc = ver.TranslatedDescription();
+      if (Desc.end() == false)
+      {
+        pkgRecords::Parser & parser = records.Lookup(Desc.FileList());
+        ShortDescription = parser.ShortDesc();
+      }
    }
    return ShortDescription;
 }
@@ -222,11 +226,14 @@ static std::string GetLongDescription(pkgCacheFile &CacheFile, pkgRecords &recor
       return EmptyDescription;
 
    pkgCache::DescIterator const Desc = ver.TranslatedDescription();
-   pkgRecords::Parser & parser = records.Lookup(Desc.FileList());
-   std::string const longdesc = parser.LongDesc();
-   if (longdesc.empty() == true)
-      return EmptyDescription;
-   return SubstVar(longdesc, "\n ", "\n  ");
+   if (Desc.end() == false)
+   {
+      pkgRecords::Parser & parser = records.Lookup(Desc.FileList());
+      std::string const longdesc = parser.LongDesc();
+      if (longdesc.empty() == false)
+        return SubstVar(longdesc, "\n ", "\n  ");
+   }
+   return EmptyDescription;
 }
                                                                        /*}}}*/
 void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,   /*{{{*/
@@ -283,7 +290,8 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,        /*{{{*/
    output = SubstVar(output, "${color:highlight}", _config->Find("APT::Color::Highlight", ""));
    output = SubstVar(output, "${color:neutral}", _config->Find("APT::Color::Neutral", ""));
    output = SubstVar(output, "${Description}", GetShortDescription(CacheFile, records, P));
-   output = SubstVar(output, "${LongDescription}", GetLongDescription(CacheFile, records, P));
+   if (output.find("${LongDescription}") != string::npos)
+      output = SubstVar(output, "${LongDescription}", GetLongDescription(CacheFile, records, P));
    output = SubstVar(output, "${ }${ }", "${ }");
    output = SubstVar(output, "${ }\n", "\n");
    output = SubstVar(output, "${ }", " ");
@@ -293,66 +301,6 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,       /*{{{*/
    out << output;
 }
                                                                        /*}}}*/
-// ShowList - Show a list                                              /*{{{*/
-// ---------------------------------------------------------------------
-/* This prints out a string of space separated words with a title and 
-   a two space indent line wraped to the current screen width. */
-bool ShowList(ostream &out,string Title,string List,string VersionsList)
-{
-   if (List.empty() == true)
-      return true;
-   // trim trailing space
-   int NonSpace = List.find_last_not_of(' ');
-   if (NonSpace != -1)
-   {
-      List = List.erase(NonSpace + 1);
-      if (List.empty() == true)
-        return true;
-   }
-
-   // Acount for the leading space
-   int ScreenWidth = ::ScreenWidth - 3;
-      
-   out << Title << endl;
-   string::size_type Start = 0;
-   string::size_type VersionsStart = 0;
-   while (Start < List.size())
-   {
-      if(_config->FindB("APT::Get::Show-Versions",false) == true &&
-         VersionsList.size() > 0) {
-         string::size_type End;
-         string::size_type VersionsEnd;
-         
-         End = List.find(' ',Start);
-         VersionsEnd = VersionsList.find('\n', VersionsStart);
-
-         out << "   " << string(List,Start,End - Start) << " (" << 
-            string(VersionsList,VersionsStart,VersionsEnd - VersionsStart) << 
-            ")" << endl;
-
-        if (End == string::npos || End < Start)
-           End = Start + ScreenWidth;
-
-         Start = End + 1;
-         VersionsStart = VersionsEnd + 1;
-      } else {
-         string::size_type End;
-
-         if (Start + ScreenWidth >= List.size())
-            End = List.size();
-         else
-            End = List.rfind(' ',Start+ScreenWidth);
-
-         if (End == string::npos || End < Start)
-            End = Start + ScreenWidth;
-         out << "  " << string(List,Start,End - Start) << endl;
-         Start = End + 1;
-      }
-   }   
-
-   return false;
-}
-                                                                       /*}}}*/
 // ShowBroken - Debugging aide                                         /*{{{*/
 // ---------------------------------------------------------------------
 /* This prints out the names of all the packages that are broken along
@@ -486,11 +434,9 @@ void ShowBroken(ostream &out, CacheFile &Cache, bool const Now)
       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);
-   }
+   SortedPackageUniverse Universe(Cache);
+   for (auto const &Pkg: Universe)
+      ShowBrokenPackage(out, &Cache, Pkg, Now);
 }
 void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now)
 {
@@ -498,98 +444,64 @@ void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now)
       return;
 
    out << _("The following packages have unmet dependencies:") << endl;
-   for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+   APT::PackageUniverse Universe(Cache);
+   for (auto const &Pkg: Universe)
       ShowBrokenPackage(out, &Cache, Pkg, Now);
 }
                                                                        /*}}}*/
 // ShowNew - Show packages to newly install                            /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 void ShowNew(ostream &out,CacheFile &Cache)
 {
-   /* Print out a list of packages that are going to be installed extra
-      to what the user asked */
-   string List;
-   string VersionsList;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-   {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      if (Cache[I].NewInstall() == true) {
-         List += I.FullName(true) + " ";
-         VersionsList += string(Cache[I].CandVersion) + "\n";
-      }
-   }
-   
-   ShowList(out,_("The following NEW packages will be installed:"),List,VersionsList);
+   SortedPackageUniverse Universe(Cache);
+   ShowList(out,_("The following NEW packages will be installed:"), Universe,
+        [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].NewInstall(); },
+        &PrettyFullName,
+        CandidateVersion(&Cache));
 }
                                                                        /*}}}*/
 // ShowDel - Show packages to delete                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 void ShowDel(ostream &out,CacheFile &Cache)
 {
-   /* Print out a list of packages that are going to be removed extra
-      to what the user asked */
-   string List;
-   string VersionsList;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-   {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      if (Cache[I].Delete() == true)
-      {
-        if ((Cache[I].iFlags & pkgDepCache::Purge) == pkgDepCache::Purge)
-           List += I.FullName(true) + "* ";
-        else
-           List += I.FullName(true) + " ";
-     
-     VersionsList += string(Cache[I].CandVersion)+ "\n";
-      }
-   }
-   
-   ShowList(out,_("The following packages will be REMOVED:"),List,VersionsList);
+   SortedPackageUniverse Universe(Cache);
+   ShowList(out,_("The following packages will be REMOVED:"), Universe,
+        [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].Delete(); },
+        [&Cache](pkgCache::PkgIterator const &Pkg)
+        {
+           std::string str = PrettyFullName(Pkg);
+           if (((*Cache)[Pkg].iFlags & pkgDepCache::Purge) == pkgDepCache::Purge)
+              str.append("*");
+           return str;
+        },
+        CandidateVersion(&Cache));
 }
                                                                        /*}}}*/
 // ShowKept - Show kept packages                                       /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 void ShowKept(ostream &out,CacheFile &Cache)
 {
-   string List;
-   string VersionsList;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-   {    
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      
-      // Not interesting
-      if (Cache[I].Upgrade() == true || Cache[I].Upgradable() == false ||
-         I->CurrentVer == 0 || Cache[I].Delete() == true)
-        continue;
-      
-      List += I.FullName(true) + " ";
-      VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
-   }
-   ShowList(out,_("The following packages have been kept back:"),List,VersionsList);
+   SortedPackageUniverse Universe(Cache);
+   ShowList(out,_("The following packages have been kept back:"), Universe,
+        [&Cache](pkgCache::PkgIterator const &Pkg)
+        {
+           return Cache[Pkg].Upgrade() == false &&
+                  Cache[Pkg].Upgradable() == true &&
+                  Pkg->CurrentVer != 0 &&
+                  Cache[Pkg].Delete() == false;
+        },
+        &PrettyFullName,
+        CurrentToCandidateVersion(&Cache));
 }
                                                                        /*}}}*/
 // ShowUpgraded - Show upgraded packages                               /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 void ShowUpgraded(ostream &out,CacheFile &Cache)
 {
-   string List;
-   string VersionsList;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-   {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      
-      // Not interesting
-      if (Cache[I].Upgrade() == false || Cache[I].NewInstall() == true)
-        continue;
-
-      List += I.FullName(true) + " ";
-      VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
-   }
-   ShowList(out,_("The following packages will be upgraded:"),List,VersionsList);
+   SortedPackageUniverse Universe(Cache);
+   ShowList(out,_("The following packages will be upgraded:"), Universe,
+        [&Cache](pkgCache::PkgIterator const &Pkg)
+        {
+           return Cache[Pkg].Upgrade() == true && Cache[Pkg].NewInstall() == false;
+        },
+        &PrettyFullName,
+        CurrentToCandidateVersion(&Cache));
 }
                                                                        /*}}}*/
 // ShowDowngraded - Show downgraded packages                           /*{{{*/
@@ -597,74 +509,73 @@ void ShowUpgraded(ostream &out,CacheFile &Cache)
 /* */
 bool ShowDowngraded(ostream &out,CacheFile &Cache)
 {
-   string List;
-   string VersionsList;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-   {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      
-      // Not interesting
-      if (Cache[I].Downgrade() == false || Cache[I].NewInstall() == true)
-        continue;
-
-      List += I.FullName(true) + " ";
-      VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
-   }
-   return ShowList(out,_("The following packages will be DOWNGRADED:"),List,VersionsList);
+   SortedPackageUniverse Universe(Cache);
+   return ShowList(out,_("The following packages will be DOWNGRADED:"), Universe,
+        [&Cache](pkgCache::PkgIterator const &Pkg)
+        {
+           return Cache[Pkg].Downgrade() == true && Cache[Pkg].NewInstall() == false;
+        },
+        &PrettyFullName,
+        CurrentToCandidateVersion(&Cache));
 }
                                                                        /*}}}*/
 // ShowHold - Show held but changed packages                           /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 bool ShowHold(ostream &out,CacheFile &Cache)
 {
-   string List;
-   string VersionsList;
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
-   {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
-      if (Cache[I].InstallVer != (pkgCache::Version *)I.CurrentVer() &&
-          I->SelectedState == pkgCache::State::Hold) {
-         List += I.FullName(true) + " ";
-                VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
-      }
-   }
-
-   return ShowList(out,_("The following held packages will be changed:"),List,VersionsList);
+   SortedPackageUniverse Universe(Cache);
+   return ShowList(out,_("The following held packages will be changed:"), Universe,
+        [&Cache](pkgCache::PkgIterator const &Pkg)
+        {
+           return Pkg->SelectedState == pkgCache::State::Hold &&
+                  Cache[Pkg].InstallVer != (pkgCache::Version *)Pkg.CurrentVer();
+        },
+        &PrettyFullName,
+        CurrentToCandidateVersion(&Cache));
 }
                                                                        /*}}}*/
 // ShowEssential - Show an essential package warning                   /*{{{*/
 // ---------------------------------------------------------------------
 /* This prints out a warning message that is not to be ignored. It shows
-   all essential packages and their dependents that are to be removed. 
+   all essential packages and their dependents that are to be removed.
    It is insanely risky to remove the dependents of an essential package! */
+struct APT_HIDDEN PrettyFullNameWithDue {
+   std::map<unsigned long long, pkgCache::PkgIterator> due;
+   PrettyFullNameWithDue() {}
+   std::string operator() (pkgCache::PkgIterator const &Pkg)
+   {
+      std::string const A = PrettyFullName(Pkg);
+      std::map<unsigned long long, pkgCache::PkgIterator>::const_iterator d = due.find(Pkg->ID);
+      if (d == due.end())
+        return A;
+
+      std::string const B = PrettyFullName(d->second);
+      std::ostringstream outstr;
+      ioprintf(outstr, _("%s (due to %s)"), A.c_str(), B.c_str());
+      return outstr.str();
+   }
+};
 bool ShowEssential(ostream &out,CacheFile &Cache)
 {
-   string List;
-   string VersionsList;
-   bool *Added = new bool[Cache->Head().PackageCount];
-   for (unsigned int I = 0; I != Cache->Head().PackageCount; I++)
-      Added[I] = false;
-   
-   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+   std::vector<bool> Added(Cache->Head().PackageCount, false);
+   APT::PackageDeque pkglist;
+   PrettyFullNameWithDue withdue;
+
+   SortedPackageUniverse Universe(Cache);
+   for (pkgCache::PkgIterator const &I: Universe)
    {
-      pkgCache::PkgIterator I(Cache,Cache.List[J]);
       if ((I->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential &&
          (I->Flags & pkgCache::Flag::Important) != pkgCache::Flag::Important)
         continue;
-      
+
       // The essential package is being removed
-      if (Cache[I].Delete() == true)
+      if (Cache[I].Delete() == false)
+        continue;
+
+      if (Added[I->ID] == false)
       {
-        if (Added[I->ID] == false)
-        {
-           Added[I->ID] = true;
-           List += I.FullName(true) + " ";
-        //VersionsList += string(Cache[I].CurVersion) + "\n"; ???
-        }
+        Added[I->ID] = true;
+        pkglist.insert(I);
       }
-      else
-        continue;
 
       if (I->CurrentVer == 0)
         continue;
@@ -676,27 +587,23 @@ bool ShowEssential(ostream &out,CacheFile &Cache)
         if (D->Type != pkgCache::Dep::PreDepends &&
             D->Type != pkgCache::Dep::Depends)
            continue;
-        
+
         pkgCache::PkgIterator P = D.SmartTargetPkg();
         if (Cache[P].Delete() == true)
         {
            if (Added[P->ID] == true)
               continue;
            Added[P->ID] = true;
-           
-           char S[300];
-           snprintf(S,sizeof(S),_("%s (due to %s) "),P.FullName(true).c_str(),I.FullName(true).c_str());
-           List += S;
-        //VersionsList += "\n"; ???
-        }       
-      }      
+
+           pkglist.insert(P);
+           withdue.due[P->ID] = I;
+        }
+      }
    }
-   
-   delete [] Added;
    return ShowList(out,_("WARNING: The following essential packages will be removed.\n"
-                        "This should NOT be done unless you know exactly what you are doing!"),List,VersionsList);
+                        "This should NOT be done unless you know exactly what you are doing!"),
+        pkglist, &AlwaysTrue, withdue, &EmptyString);
 }
-
                                                                        /*}}}*/
 // Stats - Show some statistics                                                /*{{{*/
 // ---------------------------------------------------------------------
@@ -743,8 +650,21 @@ void Stats(ostream &out,pkgDepCache &Dep)
 // YnPrompt - Yes No Prompt.                                           /*{{{*/
 // ---------------------------------------------------------------------
 /* Returns true on a Yes.*/
-bool YnPrompt(bool Default)
+bool YnPrompt(char const * const Question, bool Default)
 {
+   auto const AssumeYes = _config->FindB("APT::Get::Assume-Yes",false);
+   auto const AssumeNo = _config->FindB("APT::Get::Assume-No",false);
+   // if we ask interactively, show warnings/notices before the question
+   if (AssumeYes == false && AssumeNo == false)
+   {
+      if (_config->FindI("quiet",0) > 0)
+        _error->DumpErrors(c2out);
+      else
+        _error->DumpErrors(c2out, GlobalError::DEBUG);
+   }
+
+   c2out << Question << std::flush;
+
    /* nl_langinfo does not support LANGUAGE setting, so we unset it here
       to have the help-message (hopefully) match the expected characters */
    char * language = getenv("LANGUAGE");
@@ -772,13 +692,13 @@ bool YnPrompt(bool Default)
       free(language);
    }
 
-   if (_config->FindB("APT::Get::Assume-Yes",false) == true)
+   if (AssumeYes)
    {
       // TRANSLATOR: "Yes" answer printed for a yes/no question if --assume-yes is set
       c1out << _("Y") << std::endl;
       return true;
    }
-   else if (_config->FindB("APT::Get::Assume-No",false) == true)
+   else if (AssumeNo)
    {
       // TRANSLATOR: "No" answer printed for a yes/no question if --assume-no is set
       c1out << _("N") << std::endl;
@@ -815,8 +735,14 @@ bool YnPrompt(bool Default)
 // AnalPrompt - Annoying Yes No Prompt.                                        /*{{{*/
 // ---------------------------------------------------------------------
 /* Returns true on a Yes.*/
-bool AnalPrompt(const char *Text)
+bool AnalPrompt(std::string const &Question, const char *Text)
 {
+   if (_config->FindI("quiet",0) > 0)
+      _error->DumpErrors(c2out);
+   else
+      _error->DumpErrors(c2out, GlobalError::DEBUG);
+   c2out << Question << std::flush;
+
    char Buf[1024];
    std::cin.getline(Buf,sizeof(Buf));
    if (strcmp(Buf,Text) == 0)
@@ -824,3 +750,33 @@ bool AnalPrompt(const char *Text)
    return false;
 }
                                                                        /*}}}*/
+
+std::string PrettyFullName(pkgCache::PkgIterator const &Pkg)
+{
+   return Pkg.FullName(true);
+}
+std::string CandidateVersion(pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg)
+{
+   return (*Cache)[Pkg].CandVersion;
+}
+std::function<std::string(pkgCache::PkgIterator const &)> CandidateVersion(pkgCacheFile * const Cache)
+{
+   return std::bind(static_cast<std::string(*)(pkgCacheFile * const, pkgCache::PkgIterator const&)>(&CandidateVersion), Cache, std::placeholders::_1);
+}
+std::string CurrentToCandidateVersion(pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg)
+{
+   return std::string((*Cache)[Pkg].CurVersion) + " => " + (*Cache)[Pkg].CandVersion;
+}
+std::function<std::string(pkgCache::PkgIterator const &)> CurrentToCandidateVersion(pkgCacheFile * const Cache)
+{
+   return std::bind(static_cast<std::string(*)(pkgCacheFile * const, pkgCache::PkgIterator const&)>(&CurrentToCandidateVersion), Cache, std::placeholders::_1);
+}
+bool AlwaysTrue(pkgCache::PkgIterator const &)
+{
+      return true;
+}
+std::string EmptyString(pkgCache::PkgIterator const &)
+{
+   return std::string();
+}
+