summaryrefslogtreecommitdiffstats
path: root/apt-private
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--apt-private/CMakeLists.txt26
-rw-r--r--apt-private/acqprogress.cc368
-rw-r--r--apt-private/acqprogress.h45
-rw-r--r--apt-private/private-cachefile.cc118
-rw-r--r--apt-private/private-cachefile.h72
-rw-r--r--apt-private/private-cacheset.cc428
-rw-r--r--apt-private/private-cacheset.h130
-rw-r--r--apt-private/private-cmndline.cc598
-rw-r--r--apt-private/private-cmndline.h42
-rw-r--r--apt-private/private-depends.cc151
-rw-r--r--apt-private/private-depends.h11
-rw-r--r--apt-private/private-download.cc378
-rw-r--r--apt-private/private-download.h38
-rw-r--r--apt-private/private-install.cc1118
-rw-r--r--apt-private/private-install.h75
-rw-r--r--apt-private/private-json-hooks.cc461
-rw-r--r--apt-private/private-json-hooks.h14
-rw-r--r--apt-private/private-list.cc165
-rw-r--r--apt-private/private-list.h11
-rw-r--r--apt-private/private-main.cc90
-rw-r--r--apt-private/private-main.h15
-rw-r--r--apt-private/private-moo.cc200
-rw-r--r--apt-private/private-moo.h10
-rw-r--r--apt-private/private-output.cc801
-rw-r--r--apt-private/private-output.h116
-rw-r--r--apt-private/private-search.cc418
-rw-r--r--apt-private/private-search.h12
-rw-r--r--apt-private/private-show.cc587
-rw-r--r--apt-private/private-show.h22
-rw-r--r--apt-private/private-source.cc891
-rw-r--r--apt-private/private-source.h11
-rw-r--r--apt-private/private-sources.cc105
-rw-r--r--apt-private/private-sources.h10
-rw-r--r--apt-private/private-unmet.cc120
-rw-r--r--apt-private/private-unmet.h10
-rw-r--r--apt-private/private-update.cc148
-rw-r--r--apt-private/private-update.h10
-rw-r--r--apt-private/private-upgrade.cc66
-rw-r--r--apt-private/private-upgrade.h13
-rw-r--r--apt-private/private-utils.cc98
-rw-r--r--apt-private/private-utils.h10
41 files changed, 8012 insertions, 0 deletions
diff --git a/apt-private/CMakeLists.txt b/apt-private/CMakeLists.txt
new file mode 100644
index 0000000..88a8f97
--- /dev/null
+++ b/apt-private/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Set the version of the library
+set(MAJOR 0.0)
+set(MINOR 0)
+
+# Definition of the C++ files used to build the library - note that this
+# is expanded at CMake time, so you have to rerun cmake if you add or remove
+# a file (you can just run cmake . in the build directory)
+file(GLOB_RECURSE library "*.cc")
+file(GLOB_RECURSE headers "*.h")
+
+# Create a library using the C++ files
+add_library(apt-private SHARED ${library})
+
+# Link the library and set the SONAME
+target_link_libraries(apt-private PUBLIC apt-pkg)
+set_target_properties(apt-private PROPERTIES VERSION ${MAJOR}.${MINOR})
+set_target_properties(apt-private PROPERTIES SOVERSION ${MAJOR})
+set_target_properties(apt-private PROPERTIES CXX_VISIBILITY_PRESET hidden)
+add_version_script(apt-private)
+
+# Install the library and the headers
+install(TARGETS apt-private
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ NAMELINK_SKIP)
+
+flatify(${PROJECT_BINARY_DIR}/include/apt-private/ "${headers}")
diff --git a/apt-private/acqprogress.cc b/apt-private/acqprogress.cc
new file mode 100644
index 0000000..fa7edfc
--- /dev/null
+++ b/apt-private/acqprogress.cc
@@ -0,0 +1,368 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Progress - Command line progress meter
+
+ ##################################################################### */
+ /*}}}*/
+// Include files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire-worker.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/acqprogress.h>
+#include <apt-private/private-output.h>
+
+#include <iostream>
+#include <sstream>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// AcqTextStatus::AcqTextStatus - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+AcqTextStatus::AcqTextStatus(std::ostream &out, unsigned int &ScreenWidth,unsigned int const Quiet) :
+ pkgAcquireStatus(), out(out), ScreenWidth(ScreenWidth), LastLineLength(0), ID(0), Quiet(Quiet)
+{
+ // testcases use it to disable pulses without disabling other user messages
+ if (Quiet == 0 && _config->FindB("quiet::NoUpdate", false) == true)
+ this->Quiet = 1;
+ if (Quiet < 2 && _config->FindB("quiet::NoProgress", false) == true)
+ this->Quiet = 2;
+}
+ /*}}}*/
+// AcqTextStatus::Start - Downloading has started /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void AcqTextStatus::Start()
+{
+ pkgAcquireStatus::Start();
+ LastLineLength = 0;
+ ID = 1;
+}
+ /*}}}*/
+void AcqTextStatus::AssignItemID(pkgAcquire::ItemDesc &Itm) /*{{{*/
+{
+ /* In theory calling it from Fetch() would be enough, but to be
+ safe we call it from IMSHit and Fail as well.
+ Also, an Item can pass through multiple stages, so ensure
+ that it keeps the same number */
+ if (Itm.Owner->ID == 0)
+ Itm.Owner->ID = ID++;
+}
+ /*}}}*/
+// AcqTextStatus::IMSHit - Called when an item got a HIT response /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void AcqTextStatus::IMSHit(pkgAcquire::ItemDesc &Itm)
+{
+ if (Quiet > 1)
+ return;
+
+ AssignItemID(Itm);
+ clearLastLine();
+
+ // TRANSLATOR: Very short word to be displayed before unchanged files in 'apt-get update'
+ ioprintf(out, _("Hit:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
+ out << std::endl;
+ Update = true;
+}
+ /*}}}*/
+// AcqTextStatus::Fetch - An item has started to download /*{{{*/
+// ---------------------------------------------------------------------
+/* This prints out the short description and the expected size */
+void AcqTextStatus::Fetch(pkgAcquire::ItemDesc &Itm)
+{
+ Update = true;
+ if (Itm.Owner->Complete == true)
+ return;
+ AssignItemID(Itm);
+
+ if (Quiet > 1)
+ return;
+
+ clearLastLine();
+
+ // TRANSLATOR: Very short word to be displayed for files processed in 'apt-get update'
+ // Potentially replaced later by "Hit:", "Ign:" or "Err:" if something (bad) happens
+ ioprintf(out, _("Get:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
+ if (Itm.Owner->FileSize != 0)
+ out << " [" << SizeToStr(Itm.Owner->FileSize) << "B]";
+ out << std::endl;
+}
+ /*}}}*/
+// AcqTextStatus::Done - Completed a download /*{{{*/
+// ---------------------------------------------------------------------
+/* We don't display anything... */
+void AcqTextStatus::Done(pkgAcquire::ItemDesc &Itm)
+{
+ Update = true;
+ AssignItemID(Itm);
+}
+ /*}}}*/
+// AcqTextStatus::Fail - Called when an item fails to download /*{{{*/
+// ---------------------------------------------------------------------
+/* We print out the error text */
+void AcqTextStatus::Fail(pkgAcquire::ItemDesc &Itm)
+{
+ if (Quiet > 1)
+ return;
+
+ AssignItemID(Itm);
+ clearLastLine();
+
+ bool ShowErrorText = true;
+ if (Itm.Owner->Status == pkgAcquire::Item::StatDone || Itm.Owner->Status == pkgAcquire::Item::StatIdle)
+ {
+ // TRANSLATOR: Very short word to be displayed for files in 'apt-get update'
+ // which failed to download, but the error is ignored (compare "Err:")
+ ioprintf(out, _("Ign:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
+ if (Itm.Owner->ErrorText.empty() ||
+ _config->FindB("Acquire::Progress::Ignore::ShowErrorText", false) == false)
+ ShowErrorText = false;
+ }
+ else
+ {
+ // TRANSLATOR: Very short word to be displayed for files in 'apt-get update'
+ // which failed to download and the error is critical (compare "Ign:")
+ ioprintf(out, _("Err:%lu %s"), Itm.Owner->ID, Itm.Description.c_str());
+ }
+
+ if (ShowErrorText)
+ {
+ std::string::size_type line_start = 0;
+ std::string::size_type line_end;
+ while ((line_end = Itm.Owner->ErrorText.find_first_of("\n\r", line_start)) != std::string::npos) {
+ out << std::endl << " " << Itm.Owner->ErrorText.substr(line_start, line_end - line_start);
+ line_start = Itm.Owner->ErrorText.find_first_not_of("\n\r", line_end + 1);
+ if (line_start == std::string::npos)
+ break;
+ }
+ if (line_start == 0)
+ out << std::endl << " " << Itm.Owner->ErrorText;
+ else if (line_start != std::string::npos)
+ out << std::endl << " " << Itm.Owner->ErrorText.substr(line_start);
+ }
+ out << std::endl;
+
+ Update = true;
+}
+ /*}}}*/
+// AcqTextStatus::Stop - Finished downloading /*{{{*/
+// ---------------------------------------------------------------------
+/* This prints out the bytes downloaded and the overall average line
+ speed */
+void AcqTextStatus::Stop()
+{
+ pkgAcquireStatus::Stop();
+ if (Quiet > 1)
+ return;
+
+ clearLastLine();
+
+ if (_config->FindB("quiet::NoStatistic", false) == true)
+ return;
+
+ if (FetchedBytes != 0 && _error->PendingError() == false)
+ ioprintf(out,_("Fetched %sB in %s (%sB/s)\n"),
+ SizeToStr(FetchedBytes).c_str(),
+ TimeToStr(ElapsedTime).c_str(),
+ SizeToStr(CurrentCPS).c_str());
+}
+ /*}}}*/
+// AcqTextStatus::Pulse - Regular event pulse /*{{{*/
+// ---------------------------------------------------------------------
+/* This draws the current progress. Each line has an overall percent
+ meter and a per active item status meter along with an overall
+ bandwidth and ETA indicator. */
+bool AcqTextStatus::Pulse(pkgAcquire *Owner)
+{
+ pkgAcquireStatus::Pulse(Owner);
+
+ if (Quiet > 0)
+ return true;
+
+ std::string Line;
+ {
+ std::stringstream S;
+ for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
+ I = Owner->WorkerStep(I))
+ {
+ // There is no item running
+ if (I->CurrentItem == 0)
+ {
+ if (I->Status.empty() == false)
+ S << " [" << I->Status << "]";
+
+ continue;
+ }
+
+ // Add in the short description
+ S << " [";
+ if (I->CurrentItem->Owner->ID != 0)
+ S << std::to_string(I->CurrentItem->Owner->ID) << " ";
+ S << I->CurrentItem->ShortDesc;
+
+ // Show the short mode string
+ if (I->CurrentItem->Owner->ActiveSubprocess.empty() == false)
+ S << " " << I->CurrentItem->Owner->ActiveSubprocess;
+
+ enum {Long = 0,Medium,Short} Mode = Medium;
+ // Add the current progress
+ if (Mode == Long)
+ S << " " << std::to_string(I->CurrentItem->CurrentSize);
+ else
+ {
+ if (Mode == Medium || I->CurrentItem->TotalSize == 0)
+ S << " " << SizeToStr(I->CurrentItem->CurrentSize) << "B";
+ }
+
+ // Add the total size and percent
+ if (I->CurrentItem->TotalSize > 0 && I->CurrentItem->Owner->Complete == false)
+ {
+ if (Mode == Short)
+ ioprintf(S, " %.0f%%", (I->CurrentItem->CurrentSize*100.0)/I->CurrentItem->TotalSize);
+ else
+ ioprintf(S, "/%sB %.0f%%", SizeToStr(I->CurrentItem->TotalSize).c_str(),
+ (I->CurrentItem->CurrentSize*100.0)/I->CurrentItem->TotalSize);
+ }
+ S << "]";
+ }
+
+ // Show at least something
+ Line = S.str();
+ S.clear();
+ if (Line.empty() == true)
+ Line = _(" [Working]");
+ }
+ // Put in the percent done
+ {
+ std::stringstream S;
+ ioprintf(S, "%.0f%%", Percent);
+ S << Line;
+ Line = S.str();
+ S.clear();
+ }
+
+ /* Put in the ETA and cps meter, block off signals to prevent strangeness
+ during resizing */
+ sigset_t Sigs,OldSigs;
+ sigemptyset(&Sigs);
+ sigaddset(&Sigs,SIGWINCH);
+ sigprocmask(SIG_BLOCK,&Sigs,&OldSigs);
+
+ if (CurrentCPS != 0)
+ {
+ unsigned long long ETA = (TotalBytes - CurrentBytes)/CurrentCPS;
+ std::string Tmp = " " + SizeToStr(CurrentCPS) + "B/s " + TimeToStr(ETA);
+ size_t alignment = Line.length() + Tmp.length();
+ if (alignment < ScreenWidth)
+ {
+ alignment = ScreenWidth - alignment;
+ for (size_t i = 0; i < alignment; ++i)
+ Line.append(" ");
+ Line.append(Tmp);
+ }
+ }
+ if (Line.length() > ScreenWidth)
+ Line.erase(ScreenWidth);
+ sigprocmask(SIG_SETMASK,&OldSigs,0);
+
+ // Draw the current status
+ if (_config->FindB("Apt::Color", false) == true)
+ out << _config->Find("APT::Color::Yellow");
+ if (LastLineLength > Line.length())
+ clearLastLine();
+ else
+ out << '\r';
+ out << Line << std::flush;
+ if (_config->FindB("Apt::Color", false) == true)
+ out << _config->Find("APT::Color::Neutral") << std::flush;
+
+ LastLineLength = Line.length();
+ Update = false;
+
+ return true;
+}
+ /*}}}*/
+// AcqTextStatus::MediaChange - Media need to be swapped /*{{{*/
+// ---------------------------------------------------------------------
+/* Prompt for a media swap */
+bool AcqTextStatus::MediaChange(std::string Media, std::string Drive)
+{
+ // If we do not output on a terminal and one of the options to avoid user
+ // interaction is given, we assume that no user is present who could react
+ // on your media change request
+ if (isatty(STDOUT_FILENO) != 1 && Quiet >= 2 &&
+ (_config->FindB("APT::Get::Assume-Yes",false) == true ||
+ _config->FindB("APT::Get::Force-Yes",false) == true ||
+ _config->FindB("APT::Get::Trivial-Only",false) == true))
+
+ return false;
+
+ clearLastLine();
+ ioprintf(out,_("Media change: please insert the disc labeled\n"
+ " '%s'\n"
+ "in the drive '%s' and press [Enter]\n"),
+ Media.c_str(),Drive.c_str());
+
+ char C = 0;
+ bool bStatus = true;
+ while (C != '\n' && C != '\r')
+ {
+ int len = read(STDIN_FILENO,&C,1);
+ if(C == 'c' || len <= 0) {
+ bStatus = false;
+ break;
+ }
+ }
+
+ if(bStatus)
+ Update = true;
+ return bStatus;
+}
+ /*}}}*/
+bool AcqTextStatus::ReleaseInfoChanges(metaIndex const * const L, metaIndex const * const N, std::vector<ReleaseInfoChange> &&Changes)/*{{{*/
+{
+ if (Quiet >= 2 || isatty(STDOUT_FILENO) != 1 || isatty(STDIN_FILENO) != 1 ||
+ _config->FindB("APT::Get::Update::InteractiveReleaseInfoChanges", false) == false)
+ return pkgAcquireStatus::ReleaseInfoChanges(nullptr, nullptr, std::move(Changes));
+
+ _error->PushToStack();
+ auto const confirmed = pkgAcquireStatus::ReleaseInfoChanges(L, N, std::move(Changes));
+ if (confirmed == true)
+ {
+ _error->MergeWithStack();
+ return true;
+ }
+ clearLastLine();
+ _error->DumpErrors(out, GlobalError::NOTICE, false);
+ _error->RevertToStack();
+ return YnPrompt(_("Do you want to accept these changes and continue updating from this repository?"), false, false, out, out);
+}
+ /*}}}*/
+void AcqTextStatus::clearLastLine() { /*{{{*/
+ if (Quiet > 0 || LastLineLength == 0)
+ return;
+
+ // do not try to clear more than the (now smaller) screen
+ if (LastLineLength > ScreenWidth)
+ LastLineLength = ScreenWidth;
+
+ out << '\r';
+ for (size_t i = 0; i < LastLineLength; ++i)
+ out << ' ';
+ out << '\r' << std::flush;
+}
+ /*}}}*/
diff --git a/apt-private/acqprogress.h b/apt-private/acqprogress.h
new file mode 100644
index 0000000..87b957e
--- /dev/null
+++ b/apt-private/acqprogress.h
@@ -0,0 +1,45 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Progress - Command line progress meter
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef ACQPROGRESS_H
+#define ACQPROGRESS_H
+
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/macros.h>
+
+#include <iostream>
+#include <string>
+
+class APT_PUBLIC AcqTextStatus : public pkgAcquireStatus
+{
+ std::ostream &out;
+ unsigned int &ScreenWidth;
+ size_t LastLineLength;
+ unsigned long ID;
+ unsigned long Quiet;
+
+ APT_HIDDEN void clearLastLine();
+ APT_HIDDEN void AssignItemID(pkgAcquire::ItemDesc &Itm);
+
+ public:
+
+ virtual bool ReleaseInfoChanges(metaIndex const * const LastRelease, metaIndex const * const CurrentRelease, std::vector<ReleaseInfoChange> &&Changes) APT_OVERRIDE;
+ virtual bool MediaChange(std::string Media,std::string Drive) APT_OVERRIDE;
+ virtual void IMSHit(pkgAcquire::ItemDesc &Itm) APT_OVERRIDE;
+ virtual void Fetch(pkgAcquire::ItemDesc &Itm) APT_OVERRIDE;
+ virtual void Done(pkgAcquire::ItemDesc &Itm) APT_OVERRIDE;
+ virtual void Fail(pkgAcquire::ItemDesc &Itm) APT_OVERRIDE;
+ virtual void Start() APT_OVERRIDE;
+ virtual void Stop() APT_OVERRIDE;
+
+ bool Pulse(pkgAcquire *Owner) APT_OVERRIDE;
+
+ AcqTextStatus(std::ostream &out, unsigned int &ScreenWidth,unsigned int const Quiet);
+};
+
+#endif
diff --git a/apt-private/private-cachefile.cc b/apt-private/private-cachefile.cc
new file mode 100644
index 0000000..9d875b4
--- /dev/null
+++ b/apt-private/private-cachefile.cc
@@ -0,0 +1,118 @@
+// Include files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/upgrade.h>
+
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-output.h>
+
+#include <cstdlib>
+#include <ostream>
+#include <string.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+static bool SortPackagesByName(pkgCache * const Owner,
+ map_pointer<pkgCache::Group> const A, map_pointer<pkgCache::Group> const B)
+{
+ if (A == 0)
+ return false;
+ if (B == 0 || A == B)
+ return true;
+ pkgCache::Group const * const GA = Owner->GrpP + A;
+ pkgCache::Group const * const GB = Owner->GrpP + B;
+ return strcmp(Owner->StrP + GA->Name, Owner->StrP + GB->Name) <= 0;
+}
+SortedPackageUniverse::SortedPackageUniverse(CacheFile &Cache) :
+ PackageUniverse{Cache}, List(Cache.UniverseList)
+{
+}
+void SortedPackageUniverse::LazyInit() const
+{
+ if (List.empty() == false)
+ return;
+ pkgCache * const Owner = data();
+ // In Multi-Arch systems Grps are easier to sort than Pkgs
+ std::vector<map_pointer<pkgCache::Group>> GrpList;
+ List.reserve(Owner->Head().GroupCount);
+ for (pkgCache::GrpIterator I{Owner->GrpBegin()}; I.end() != true; ++I)
+ GrpList.emplace_back(I - Owner->GrpP);
+ std::stable_sort(GrpList.begin(), GrpList.end(), std::bind( &SortPackagesByName, Owner, std::placeholders::_1, std::placeholders::_2 ));
+ List.reserve(Owner->Head().PackageCount);
+ for (auto G : GrpList)
+ {
+ pkgCache::GrpIterator const Grp(*Owner, Owner->GrpP + G);
+ for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
+ List.emplace_back(P - Owner->PkgP);
+ }
+}
+// CacheFile::CheckDeps - Open the cache file /*{{{*/
+// ---------------------------------------------------------------------
+/* This routine generates the caches and then opens the dependency cache
+ and verifies that the system is OK. */
+bool CacheFile::CheckDeps(bool AllowBroken)
+{
+ bool FixBroken = _config->FindB("APT::Get::Fix-Broken",false);
+
+ if (_error->PendingError() == true)
+ return false;
+
+ // Check that the system is OK
+ if (DCache->DelCount() != 0 || DCache->InstCount() != 0)
+ return _error->Error("Internal error, non-zero counts");
+
+ // Apply corrections for half-installed packages
+ if (pkgApplyStatus(*DCache) == false)
+ return false;
+
+ if (_config->FindB("APT::Get::Fix-Policy-Broken",false) == true)
+ {
+ FixBroken = true;
+ if ((DCache->PolicyBrokenCount() > 0))
+ {
+ // upgrade all policy-broken packages with ForceImportantDeps=True
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); !I.end(); ++I)
+ if ((*DCache)[I].NowPolicyBroken() == true)
+ DCache->MarkInstall(I,true,0, false, true);
+ }
+ }
+
+ // Nothing is broken
+ if (DCache->BrokenCount() == 0 || AllowBroken == true)
+ return true;
+
+ // Attempt to fix broken things
+ if (FixBroken == true)
+ {
+ c1out << _("Correcting dependencies...") << flush;
+ if (pkgFixBroken(*DCache) == false || DCache->BrokenCount() != 0)
+ {
+ c1out << _(" failed.") << endl;
+ ShowBroken(c1out,*this,true);
+
+ return _error->Error(_("Unable to correct dependencies"));
+ }
+ if (pkgMinimizeUpgrade(*DCache) == false)
+ return _error->Error(_("Unable to minimize the upgrade set"));
+
+ c1out << _(" Done") << endl;
+ }
+ else
+ {
+ c1out << _("You might want to run 'apt --fix-broken install' to correct these.") << endl;
+ ShowBroken(c1out,*this,true);
+ return _error->Error(_("Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution)."));
+ }
+
+ return true;
+}
+ /*}}}*/
diff --git a/apt-private/private-cachefile.h b/apt-private/private-cachefile.h
new file mode 100644
index 0000000..ccd4710
--- /dev/null
+++ b/apt-private/private-cachefile.h
@@ -0,0 +1,72 @@
+#ifndef APT_PRIVATE_CACHEFILE_H
+#define APT_PRIVATE_CACHEFILE_H
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/sourcelist.h>
+
+// class CacheFile - Cover class for some dependency cache functions /*{{{*/
+class APT_PUBLIC CacheFile : public pkgCacheFile
+{
+ public:
+ std::vector<map_pointer<pkgCache::Package>> UniverseList;
+
+ bool CheckDeps(bool AllowBroken = false);
+ bool BuildCaches(bool WithLock = true)
+ {
+ OpTextProgress Prog(*_config);
+ if (pkgCacheFile::BuildCaches(&Prog,WithLock) == false)
+ return false;
+ return true;
+ }
+ bool Open(bool WithLock = true)
+ {
+ OpTextProgress Prog(*_config);
+ return pkgCacheFile::Open(&Prog,WithLock);
+ };
+ bool OpenForInstall()
+ {
+ if (_config->FindB("APT::Get::Print-URIs") == true)
+ return Open(false);
+ else
+ return Open(true);
+ }
+};
+ /*}}}*/
+
+class SortedPackageUniverse : public APT::PackageUniverse
+{
+ std::vector<map_pointer<pkgCache::Package>> &List;
+ void LazyInit() const;
+
+public:
+ explicit SortedPackageUniverse(CacheFile &Cache);
+
+ class const_iterator : public APT::Container_iterator_base<APT::PackageContainerInterface, SortedPackageUniverse, SortedPackageUniverse::const_iterator, std::vector<map_pointer<pkgCache::Package>>::const_iterator, pkgCache::PkgIterator>
+ {
+ pkgCache * const Cache;
+ public:
+ inline pkgCache::PkgIterator getType(void) const
+ {
+ if (*_iter == 0) return pkgCache::PkgIterator(*Cache);
+ return pkgCache::PkgIterator(*Cache, Cache->PkgP + *_iter);
+ }
+ explicit const_iterator(pkgCache * const Owner, std::vector<map_pointer<pkgCache::Package>>::const_iterator i):
+ Container_iterator_base<APT::PackageContainerInterface, SortedPackageUniverse, SortedPackageUniverse::const_iterator, std::vector<map_pointer<pkgCache::Package>>::const_iterator, pkgCache::PkgIterator>(i), Cache(Owner) {}
+
+ };
+ typedef const_iterator iterator;
+
+ const_iterator begin() const { LazyInit(); return const_iterator(data(), List.begin()); }
+ const_iterator end() const { LazyInit(); return const_iterator(data(), List.end()); }
+ const_iterator cbegin() const { LazyInit(); return const_iterator(data(), List.begin()); }
+ const_iterator cend() const { LazyInit(); return const_iterator(data(), List.end()); }
+ iterator begin() { LazyInit(); return iterator(data(), List.begin()); }
+ iterator end() { LazyInit(); return iterator(data(), List.end()); }
+};
+
+#endif
diff --git a/apt-private/private-cacheset.cc b/apt-private/private-cacheset.cc
new file mode 100644
index 0000000..2a5afac
--- /dev/null
+++ b/apt-private/private-cacheset.cc
@@ -0,0 +1,428 @@
+#include <config.h>
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cachefilter.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cacheset.h>
+
+#include <stddef.h>
+
+#include <apti18n.h>
+
+bool GetLocalitySortedVersionSet(pkgCacheFile &CacheFile, /*{{{*/
+ APT::VersionContainerInterface * const vci,
+ OpProgress * const progress)
+{
+ Matcher null_matcher = Matcher();
+ return GetLocalitySortedVersionSet(CacheFile, vci,
+ null_matcher, progress);
+}
+bool GetLocalitySortedVersionSet(pkgCacheFile &CacheFile,
+ APT::VersionContainerInterface * const vci,
+ Matcher &matcher,
+ OpProgress * const progress)
+{
+ pkgCache * const Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == nullptr))
+ return false;
+ if (progress != nullptr)
+ progress->SubProgress(Cache->Head().PackageCount, _("Sorting"));
+
+ pkgDepCache * const DepCache = CacheFile.GetDepCache();
+ if (unlikely(DepCache == nullptr))
+ return false;
+ APT::CacheSetHelper helper(false);
+
+ int Done=0;
+
+ bool const insertCurrentVer = _config->FindB("APT::Cmd::Installed", false);
+ bool const insertUpgradable = _config->FindB("APT::Cmd::Upgradable", false);
+ bool const insertManualInstalled = _config->FindB("APT::Cmd::Manual-Installed", false);
+
+ for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
+ {
+ if (progress != NULL)
+ {
+ if (Done % 500 == 0)
+ progress->Progress(Done);
+ ++Done;
+ }
+
+ // exclude virtual pkgs
+ if (P->VersionList == 0)
+ continue;
+
+ if ((matcher)(P) == false)
+ continue;
+
+ pkgDepCache::StateCache &state = (*DepCache)[P];
+ if (insertCurrentVer == true)
+ {
+ if (P->CurrentVer != 0)
+ vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::INSTALLED, helper);
+ }
+ else if (insertUpgradable == true)
+ {
+ if(P.CurrentVer() && state.Upgradable())
+ vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::CANDIDATE, helper);
+ }
+ else if (insertManualInstalled == true)
+ {
+ if (P.CurrentVer() &&
+ ((*DepCache)[P].Flags & pkgCache::Flag::Auto) == false)
+ vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::CANDIDATE, helper);
+ }
+ else
+ {
+ if (vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::CANDIDATE, helper) == false)
+ {
+ // no candidate, this may happen for packages in
+ // dpkg "deinstall ok config-file" state - we pick the first ver
+ // (which should be the only one)
+ vci->insert(P.VersionList());
+ }
+ }
+ }
+ if (progress != NULL)
+ progress->Done();
+ return true;
+}
+ /*}}}*/
+
+// CacheSetHelper saving virtual packages /*{{{*/
+pkgCache::VerIterator CacheSetHelperVirtuals::canNotGetVersion(
+ enum CacheSetHelper::VerSelector const select,
+ pkgCacheFile &Cache,
+ pkgCache::PkgIterator const &Pkg)
+{
+ if (select == NEWEST || select == CANDIDATE || select == ALL)
+ virtualPkgs.insert(Pkg);
+ return CacheSetHelper::canNotGetVersion(select, Cache, Pkg);
+}
+void CacheSetHelperVirtuals::canNotFindVersion(
+ enum CacheSetHelper::VerSelector const select,
+ APT::VersionContainerInterface * vci,
+ pkgCacheFile &Cache,
+ pkgCache::PkgIterator const &Pkg)
+{
+ if (select == NEWEST || select == CANDIDATE || select == ALL)
+ virtualPkgs.insert(Pkg);
+ return CacheSetHelper::canNotFindVersion(select, vci, Cache, Pkg);
+}
+static pkgCache::PkgIterator canNotFindPkgName_impl(pkgCacheFile &Cache, std::string const &str)
+{
+ std::string pkg = str;
+ size_t const archfound = pkg.find_last_of(':');
+ std::string arch;
+ if (archfound != std::string::npos) {
+ arch = pkg.substr(archfound+1);
+ pkg.erase(archfound);
+ if (arch == "all" || arch == "native")
+ arch = _config->Find("APT::Architecture");
+ }
+
+ // If we don't find 'foo:amd64' look for 'foo:amd64:any'.
+ // Note: we prepare for an error here as if foo:amd64 does not exist,
+ // but foo:amd64:any it means that this package is only referenced in a
+ // (architecture specific) dependency. We do not add to virtualPkgs directly
+ // as we can't decide from here which error message has to be printed.
+ // FIXME: This doesn't match 'barbarian' architectures
+ pkgCache::PkgIterator Pkg(Cache, 0);
+ std::vector<std::string> const archs = APT::Configuration::getArchitectures();
+ if (archfound == std::string::npos)
+ {
+ for (auto const &a : archs)
+ {
+ Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + a, "any");
+ if (Pkg.end() == false && Pkg->ProvidesList != 0)
+ break;
+ }
+ if (Pkg.end() == true)
+ for (auto const &a : archs)
+ {
+ Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + a, "any");
+ if (Pkg.end() == false)
+ break;
+ }
+ }
+ else
+ {
+ Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + arch, "any");
+ if (Pkg.end() == true)
+ {
+ APT::CacheFilter::PackageArchitectureMatchesSpecification pams(arch);
+ for (auto const &a : archs)
+ {
+ if (pams(a.c_str()) == false)
+ continue;
+ Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + a, "any");
+ if (Pkg.end() == false)
+ break;
+ }
+ }
+ }
+ return Pkg;
+}
+pkgCache::PkgIterator CacheSetHelperVirtuals::canNotFindPkgName(pkgCacheFile &Cache, std::string const &str)
+{
+ pkgCache::PkgIterator const Pkg = canNotFindPkgName_impl(Cache, str);
+ if (Pkg.end())
+ return APT::CacheSetHelper::canNotFindPkgName(Cache, str);
+ return Pkg;
+}
+CacheSetHelperVirtuals::CacheSetHelperVirtuals(bool const ShowErrors, GlobalError::MsgType const &ErrorType) :
+ CacheSetHelper{ShowErrors, ErrorType}
+{}
+ /*}}}*/
+
+// CacheSetHelperAPTGet - responsible for message telling from the CacheSets/*{{{*/
+CacheSetHelperAPTGet::CacheSetHelperAPTGet(std::ostream &pout) :
+ APT::CacheSetHelper{true}, out(pout)
+{
+ explicitlyNamed = true;
+}
+void CacheSetHelperAPTGet::showPackageSelection(pkgCache::PkgIterator const &pkg, enum PkgSelector const select,
+ std::string const &pattern)
+{
+ switch (select)
+ {
+ case REGEX:
+ showRegExSelection(pkg, pattern);
+ break;
+ case TASK:
+ showTaskSelection(pkg, pattern);
+ break;
+ case FNMATCH:
+ showFnmatchSelection(pkg, pattern);
+ break;
+ default:
+ APT::CacheSetHelper::showPackageSelection(pkg, select, pattern);
+ break;
+ }
+}
+void CacheSetHelperAPTGet::showTaskSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern)
+{
+ ioprintf(out, _("Note, selecting '%s' for task '%s'\n"),
+ Pkg.FullName(true).c_str(), pattern.c_str());
+ explicitlyNamed = false;
+}
+void CacheSetHelperAPTGet::showFnmatchSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern)
+{
+ ioprintf(out, _("Note, selecting '%s' for glob '%s'\n"),
+ Pkg.FullName(true).c_str(), pattern.c_str());
+ explicitlyNamed = false;
+}
+void CacheSetHelperAPTGet::showRegExSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern)
+{
+ ioprintf(out, _("Note, selecting '%s' for regex '%s'\n"),
+ Pkg.FullName(true).c_str(), pattern.c_str());
+ explicitlyNamed = false;
+}
+void CacheSetHelperAPTGet::showVersionSelection(pkgCache::PkgIterator const &Pkg,
+ pkgCache::VerIterator const &Ver, enum VerSelector const select, std::string const &pattern)
+{
+ switch (select)
+ {
+ case RELEASE:
+ case VERSIONNUMBER:
+ if (pattern == Ver.VerStr())
+ return;
+ selectedByRelease.push_back(make_pair(Ver, pattern));
+ break;
+ default:
+ return APT::CacheSetHelper::showVersionSelection(Pkg, Ver, select, pattern);
+ }
+}
+
+bool CacheSetHelperAPTGet::showVirtualPackageErrors(pkgCacheFile &Cache)
+{
+ if (virtualPkgs.empty() == true)
+ return true;
+ for (APT::PackageSet::const_iterator Pkg = virtualPkgs.begin();
+ Pkg != virtualPkgs.end(); ++Pkg) {
+ if (Pkg->ProvidesList != 0) {
+ ioprintf(c1out,_("Package %s is a virtual package provided by:\n"),
+ Pkg.FullName(true).c_str());
+
+ pkgCache::PrvIterator I = Pkg.ProvidesList();
+ unsigned short provider = 0;
+ for (; I.end() == false; ++I) {
+ pkgCache::PkgIterator const OPkg = I.OwnerPkg();
+
+ if (Cache[OPkg].CandidateVerIter(Cache) == I.OwnerVer())
+ {
+ c1out << " " << OPkg.FullName(true) << " " << I.OwnerVer().VerStr();
+ if (Cache[OPkg].Install() == true && Cache[OPkg].NewInstall() == false)
+ c1out << _(" [Installed]");
+ c1out << std::endl;
+ ++provider;
+ }
+ }
+ // if we found no candidate which provide this package, show non-candidates
+ if (provider == 0)
+ for (I = Pkg.ProvidesList(); I.end() == false; ++I)
+ c1out << " " << I.OwnerPkg().FullName(true) << " " << I.OwnerVer().VerStr()
+ << _(" [Not candidate version]") << std::endl;
+ else
+ out << _("You should explicitly select one to install.") << std::endl;
+ } else {
+ ioprintf(c1out,
+ _("Package %s is not available, but is referred to by another package.\n"
+ "This may mean that the package is missing, has been obsoleted, or\n"
+ "is only available from another source\n"),Pkg.FullName(true).c_str());
+
+ std::vector<bool> Seen(Cache.GetPkgCache()->Head().PackageCount, false);
+ APT::PackageList pkglist;
+ for (pkgCache::DepIterator Dep = Pkg.RevDependsList();
+ Dep.end() == false; ++Dep) {
+ if (Dep->Type != pkgCache::Dep::Replaces)
+ continue;
+ pkgCache::PkgIterator const DP = Dep.ParentPkg();
+ if (Seen[DP->ID] == true)
+ continue;
+ Seen[DP->ID] = true;
+ pkglist.insert(DP);
+ }
+ ShowList(c1out, _("However the following packages replace it:"), pkglist,
+ &AlwaysTrue, &PrettyFullName, &EmptyString);
+ }
+ c1out << std::endl;
+ }
+ return false;
+}
+pkgCache::VerIterator CacheSetHelperAPTGet::canNotGetVersion(enum VerSelector const select, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg)
+{
+ switch (select)
+ {
+ case NEWEST:
+ return canNotFindNewestVer(Cache, Pkg);
+ case CANDIDATE:
+ return canNotFindCandidateVer(Cache, Pkg);
+ default:
+ return APT::CacheSetHelper::canNotGetVersion(select, Cache, Pkg);
+ }
+}
+void CacheSetHelperAPTGet::canNotFindVersion(enum VerSelector const select, APT::VersionContainerInterface * const vci, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg)
+{
+ switch (select)
+ {
+ case NEWEST:
+ canNotFindNewestVer(Cache, Pkg);
+ break;
+ case CANDIDATE:
+ canNotFindCandidateVer(Cache, Pkg);
+ break;
+ default:
+ return APT::CacheSetHelper::canNotFindVersion(select, vci, Cache, Pkg);
+ }
+}
+
+pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindCandidateVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg)
+{
+ APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::CANDIDATE);
+ if (verset.empty() == false)
+ return *(verset.begin());
+ else if (ShowError == true) {
+ _error->Error(_("Package '%s' has no installation candidate"),Pkg.FullName(true).c_str());
+ virtualPkgs.insert(Pkg);
+ }
+ return pkgCache::VerIterator(Cache, 0);
+}
+pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindNewestVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg)
+{
+ if (Pkg->ProvidesList != 0)
+ {
+ APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::NEWEST);
+ if (verset.empty() == false)
+ return *(verset.begin());
+ if (ShowError == true)
+ ioprintf(out, _("Virtual packages like '%s' can't be removed\n"), Pkg.FullName(true).c_str());
+ }
+ else
+ {
+ pkgCache::GrpIterator Grp = Pkg.Group();
+ pkgCache::PkgIterator P = Grp.PackageList();
+ for (; P.end() != true; P = Grp.NextPkg(P))
+ {
+ if (P == Pkg)
+ continue;
+ if (P->CurrentVer != 0) {
+ // TRANSLATORS: Note, this is not an interactive question
+ ioprintf(c1out,_("Package '%s' is not installed, so not removed. Did you mean '%s'?\n"),
+ Pkg.FullName(true).c_str(), P.FullName(true).c_str());
+ break;
+ }
+ }
+ if (P.end() == true)
+ ioprintf(c1out,_("Package '%s' is not installed, so not removed\n"),Pkg.FullName(true).c_str());
+ }
+ return pkgCache::VerIterator(Cache, 0);
+}
+APT::VersionSet CacheSetHelperAPTGet::tryVirtualPackage(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg,
+ CacheSetHelper::VerSelector const select)
+{
+ /* This is a pure virtual package and there is a single available
+ candidate providing it. */
+ if (unlikely(Cache[Pkg].CandidateVer != 0) || Pkg->ProvidesList == 0)
+ return APT::VersionSet();
+
+ pkgCache::PkgIterator Prov;
+ bool found_one = false;
+ for (pkgCache::PrvIterator P = Pkg.ProvidesList(); P; ++P) {
+ pkgCache::VerIterator const PVer = P.OwnerVer();
+ pkgCache::PkgIterator const PPkg = PVer.ParentPkg();
+
+ /* Ignore versions that are not a candidate. */
+ if (Cache[PPkg].CandidateVer != PVer)
+ continue;
+
+ if (found_one == false) {
+ Prov = PPkg;
+ found_one = true;
+ } else if (PPkg != Prov) {
+ // same group, so it's a foreign package
+ if (PPkg->Group == Prov->Group) {
+ // do we already have the requested arch?
+ if (strcmp(Pkg.Arch(), Prov.Arch()) == 0 ||
+ strcmp(Prov.Arch(), "all") == 0 ||
+ unlikely(strcmp(PPkg.Arch(), Prov.Arch()) == 0)) // packages have only on candidate, but just to be sure
+ continue;
+ // see which architecture we prefer more and switch to it
+ std::vector<std::string> archs = APT::Configuration::getArchitectures();
+ if (std::find(archs.begin(), archs.end(), PPkg.Arch()) < std::find(archs.begin(), archs.end(), Prov.Arch()))
+ Prov = PPkg;
+ continue;
+ }
+ found_one = false; // we found at least two
+ break;
+ }
+ }
+
+ if (found_one == true) {
+ ioprintf(out, _("Note, selecting '%s' instead of '%s'\n"),
+ Prov.FullName(true).c_str(), Pkg.FullName(true).c_str());
+ return APT::VersionSet::FromPackage(Cache, Prov, select, *this);
+ }
+ return APT::VersionSet();
+}
+pkgCache::PkgIterator CacheSetHelperAPTGet::canNotFindPkgName(pkgCacheFile &Cache, std::string const &str)
+{
+ pkgCache::PkgIterator Pkg = canNotFindPkgName_impl(Cache, str);
+ if (Pkg.end())
+ {
+ Pkg = APT::CacheSetHelper::canNotFindPkgName(Cache, str);
+ if (Pkg.end() && ShowError)
+ {
+ notFound.insert(str);
+ }
+ }
+ return Pkg;
+}
+ /*}}}*/
diff --git a/apt-private/private-cacheset.h b/apt-private/private-cacheset.h
new file mode 100644
index 0000000..8848aa3
--- /dev/null
+++ b/apt-private/private-cacheset.h
@@ -0,0 +1,130 @@
+#ifndef APT_PRIVATE_CACHESET_H
+#define APT_PRIVATE_CACHESET_H
+
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/macros.h>
+
+#include <apt-private/private-output.h>
+
+#include <list>
+#include <set>
+#include <string>
+#include <vector>
+
+class OpProgress;
+
+class VerIteratorWithCaching
+{
+ const pkgCache::VerIterator iter;
+ const pkgCache::DescFile * descFile;
+public:
+
+ // cppcheck-suppress noExplicitConstructor
+ VerIteratorWithCaching(const pkgCache::VerIterator& iter) :
+ iter(iter),
+ descFile(iter->DescriptionList != 0
+ ? (const pkgCache::DescFile *) iter.TranslatedDescription().FileList()
+ : nullptr)
+ {}
+ const pkgCache::DescFile * CachedDescFile() const { return descFile; }
+ operator pkgCache::VerIterator() const { return iter; }
+ map_id_t ID() const { return iter->ID; }
+};
+
+struct VersionSortDescriptionLocality /*{{{*/
+{
+ bool operator () (const VerIteratorWithCaching &v_lhs,
+ const VerIteratorWithCaching &v_rhs)
+ {
+ pkgCache::DescFile const *A = v_lhs.CachedDescFile();
+ pkgCache::DescFile const *B = v_rhs.CachedDescFile();
+
+ if (A == nullptr)
+ {
+ if (B == nullptr)
+ return v_lhs.ID() < v_rhs.ID();
+ return true;
+ }
+ else if (B == nullptr)
+ return false;
+
+ if (A->File == B->File)
+ {
+ if (A->Offset == B->Offset)
+ return v_lhs.ID() < v_rhs.ID();
+ return A->Offset < B->Offset;
+ }
+
+ return A->File < B->File;
+ }
+};
+ /*}}}*/
+// sorted by locality which makes iterating much faster
+typedef APT::VersionContainer<
+ std::set<VerIteratorWithCaching,
+ VersionSortDescriptionLocality> > LocalitySortedVersionSet;
+
+class Matcher {
+public:
+ virtual bool operator () (const pkgCache::PkgIterator &/*P*/) {
+ return true;}
+};
+
+// FIXME: add default argument for OpProgress (or overloaded function)
+bool GetLocalitySortedVersionSet(pkgCacheFile &CacheFile,
+ APT::VersionContainerInterface * const vci,
+ Matcher &matcher,
+ OpProgress * const progress);
+bool GetLocalitySortedVersionSet(pkgCacheFile &CacheFile,
+ APT::VersionContainerInterface * const vci,
+ OpProgress * const progress);
+
+
+// CacheSetHelper saving virtual packages /*{{{*/
+class CacheSetHelperVirtuals: public APT::CacheSetHelper {
+public:
+ APT::PackageSet virtualPkgs;
+
+ virtual pkgCache::VerIterator canNotGetVersion(enum CacheSetHelper::VerSelector const select, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) APT_OVERRIDE;
+ virtual void canNotFindVersion(enum CacheSetHelper::VerSelector const select, APT::VersionContainerInterface * vci, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) APT_OVERRIDE;
+ virtual pkgCache::PkgIterator canNotFindPkgName(pkgCacheFile &Cache, std::string const &str) APT_OVERRIDE;
+
+ CacheSetHelperVirtuals(bool const ShowErrors = true, GlobalError::MsgType const &ErrorType = GlobalError::NOTICE);
+};
+ /*}}}*/
+
+// CacheSetHelperAPTGet - responsible for message telling from the CacheSets/*{{{*/
+class CacheSetHelperAPTGet : public APT::CacheSetHelper {
+ /** \brief stream message should be printed to */
+ std::ostream &out;
+ /** \brief were things like Task or RegEx used to select packages? */
+ bool explicitlyNamed;
+
+ APT::PackageSet virtualPkgs;
+public:
+ std::list<std::pair<pkgCache::VerIterator, std::string> > selectedByRelease;
+ std::set<std::string> notFound;
+
+ explicit CacheSetHelperAPTGet(std::ostream &out);
+
+ virtual void showPackageSelection(pkgCache::PkgIterator const &Pkg, enum PkgSelector const select, std::string const &pattern) APT_OVERRIDE;
+ void showTaskSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern);
+ void showFnmatchSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern);
+ void showRegExSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern);
+ void showVersionSelection(pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver, enum VerSelector const select, std::string const &pattern) APT_OVERRIDE;
+ bool showVirtualPackageErrors(pkgCacheFile &Cache);
+
+ pkgCache::VerIterator canNotGetVersion(enum VerSelector const select, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) APT_OVERRIDE;
+ void canNotFindVersion(enum VerSelector const select, APT::VersionContainerInterface * const vci, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) APT_OVERRIDE;
+ pkgCache::VerIterator canNotFindCandidateVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg);
+ pkgCache::VerIterator canNotFindNewestVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg);
+ virtual pkgCache::PkgIterator canNotFindPkgName(pkgCacheFile &Cache, std::string const &str) APT_OVERRIDE;
+
+ APT::VersionSet tryVirtualPackage(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg,
+ CacheSetHelper::VerSelector const select);
+
+ inline bool allPkgNamedExplicitly() const { return explicitlyNamed; }
+};
+ /*}}}*/
+
+#endif
diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc
new file mode 100644
index 0000000..2884dcc
--- /dev/null
+++ b/apt-private/private-cmndline.cc
@@ -0,0 +1,598 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <vector>
+
+#include <apti18n.h>
+ /*}}}*/
+
+APT_NONNULL(1, 2)
+static bool CmdMatches_fn(char const *const Cmd, char const *const Match)
+{
+ return strcmp(Cmd, Match) == 0;
+}
+template <typename... Tail>
+APT_NONNULL(1, 2)
+static bool CmdMatches_fn(char const *const Cmd, char const *const Match, Tail... MoreMatches)
+{
+ return CmdMatches_fn(Cmd, Match) || CmdMatches_fn(Cmd, MoreMatches...);
+}
+#define addArg(w, x, y, z) Args.emplace_back(CommandLine::MakeArgs(w, x, y, z))
+#define CmdMatches(...) (Cmd != nullptr && CmdMatches_fn(Cmd, __VA_ARGS__))
+
+static bool addArgumentsAPTCache(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("depends", "rdepends", "xvcg", "dotty"))
+ {
+ addArg('i', "important", "APT::Cache::Important", 0);
+ addArg(0, "installed", "APT::Cache::Installed", 0);
+ addArg(0, "pre-depends", "APT::Cache::ShowPre-Depends", 0);
+ addArg(0, "depends", "APT::Cache::ShowDepends", 0);
+ addArg(0, "recommends", "APT::Cache::ShowRecommends", 0);
+ addArg(0, "suggests", "APT::Cache::ShowSuggests", 0);
+ addArg(0, "replaces", "APT::Cache::ShowReplaces", 0);
+ addArg(0, "breaks", "APT::Cache::ShowBreaks", 0);
+ addArg(0, "conflicts", "APT::Cache::ShowConflicts", 0);
+ addArg(0, "enhances", "APT::Cache::ShowEnhances", 0);
+ addArg(0, "recurse", "APT::Cache::RecurseDepends", 0);
+ addArg(0, "implicit", "APT::Cache::ShowImplicit", 0);
+ }
+ else if (CmdMatches("search"))
+ {
+ addArg('n', "names-only", "APT::Cache::NamesOnly", 0);
+ addArg('f', "full", "APT::Cache::ShowFull", 0);
+ }
+ else if (CmdMatches("show") | CmdMatches("info"))
+ {
+ addArg('a', "all-versions", "APT::Cache::AllVersions", 0);
+ }
+ else if (CmdMatches("pkgnames"))
+ {
+ addArg(0, "all-names", "APT::Cache::AllNames", 0);
+ }
+ else if (CmdMatches("unmet"))
+ {
+ addArg('i', "important", "APT::Cache::Important", 0);
+ }
+ else if (CmdMatches("showsrc"))
+ {
+ addArg(0,"only-source","APT::Cache::Only-Source",0);
+ }
+ else if (CmdMatches("gencaches", "showpkg", "stats", "dump",
+ "dumpavail", "showauto", "policy", "madison"))
+ ;
+ else
+ return false;
+
+ bool const found_something = Args.empty() == false;
+
+ // FIXME: move to the correct command(s)
+ addArg('g', "generate", "APT::Cache::Generate", 0);
+ addArg('t', "target-release", "APT::Default-Release", CommandLine::HasArg);
+ addArg('t', "default-release", "APT::Default-Release", CommandLine::HasArg);
+
+ addArg('p', "pkg-cache", "Dir::Cache::pkgcache", CommandLine::HasArg);
+ addArg('s', "src-cache", "Dir::Cache::srcpkgcache", CommandLine::HasArg);
+ addArg(0, "with-source", "APT::Sources::With::", CommandLine::HasArg);
+
+ return found_something;
+}
+ /*}}}*/
+static bool addArgumentsAPTCDROM(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("add", "ident") == false)
+ return false;
+
+ // FIXME: move to the correct command(s)
+ addArg(0, "auto-detect", "Acquire::cdrom::AutoDetect", CommandLine::Boolean);
+ addArg('d', "cdrom", "Acquire::cdrom::mount", CommandLine::HasArg);
+ addArg('r', "rename", "APT::CDROM::Rename", 0);
+ addArg('m', "no-mount", "APT::CDROM::NoMount", 0);
+ addArg('f', "fast", "APT::CDROM::Fast", 0);
+ addArg('n', "just-print", "APT::CDROM::NoAct", 0);
+ addArg('n', "recon", "APT::CDROM::NoAct", 0);
+ addArg('n', "no-act", "APT::CDROM::NoAct", 0);
+ addArg('a', "thorough", "APT::CDROM::Thorough", 0);
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTConfig(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("dump"))
+ {
+ addArg(0,"empty","APT::Config::Dump::EmptyValue",CommandLine::Boolean);
+ addArg(0,"format","APT::Config::Dump::Format",CommandLine::HasArg);
+ }
+ else if (CmdMatches("shell"))
+ ;
+ else
+ return false;
+
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTDumpSolver(std::vector<CommandLine::Args> &Args, char const * const)/*{{{*/
+{
+ addArg(0,"user","APT::Solver::RunAsUser",CommandLine::HasArg);
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTExtractTemplates(std::vector<CommandLine::Args> &Args, char const * const)/*{{{*/
+{
+ addArg('t',"tempdir","APT::ExtractTemplates::TempDir",CommandLine::HasArg);
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTFTPArchive(std::vector<CommandLine::Args> &Args, char const * const)/*{{{*/
+{
+ addArg(0,"md5","APT::FTPArchive::MD5",0);
+ addArg(0,"sha1","APT::FTPArchive::SHA1",0);
+ addArg(0,"sha256","APT::FTPArchive::SHA256",0);
+ addArg(0,"sha512","APT::FTPArchive::SHA512",0);
+ addArg('d',"db","APT::FTPArchive::DB",CommandLine::HasArg);
+ addArg('s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg);
+ addArg(0,"delink","APT::FTPArchive::DeLinkAct",0);
+ addArg(0,"readonly","APT::FTPArchive::ReadOnlyDB",0);
+ addArg(0,"contents","APT::FTPArchive::Contents",0);
+ addArg('a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg);
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTInternalPlanner(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
+{
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTInternalSolver(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
+{
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTHelper(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("cat-file"))
+ {
+ addArg('C', "compress", "Apt-Helper::Cat-File::Compress",CommandLine::HasArg);
+ }
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("install", "reinstall", "remove", "purge", "upgrade", "dist-upgrade",
+ "dselect-upgrade", "autoremove", "autopurge", "full-upgrade"))
+ {
+ addArg(0, "show-progress", "DpkgPM::Progress", 0);
+ addArg('f', "fix-broken", "APT::Get::Fix-Broken", 0);
+ addArg(0, "purge", "APT::Get::Purge", 0);
+ addArg('V',"verbose-versions","APT::Get::Show-Versions",0);
+ addArg(0, "autoremove", "APT::Get::AutomaticRemove", 0);
+ addArg(0, "auto-remove", "APT::Get::AutomaticRemove", 0);
+ addArg(0, "reinstall", "APT::Get::ReInstall", 0);
+ addArg(0, "solver", "APT::Solver", CommandLine::HasArg);
+ addArg(0, "planner", "APT::Planner", CommandLine::HasArg);
+ if (CmdMatches("upgrade"))
+ {
+ addArg(0, "new-pkgs", "APT::Get::Upgrade-Allow-New",
+ CommandLine::Boolean);
+ }
+ }
+ else if (CmdMatches("update"))
+ {
+ addArg(0, "list-cleanup", "APT::Get::List-Cleanup", 0);
+ addArg(0, "allow-insecure-repositories", "Acquire::AllowInsecureRepositories", 0);
+ addArg(0, "allow-weak-repositories", "Acquire::AllowWeakRepositories", 0);
+ addArg(0, "allow-releaseinfo-change", "Acquire::AllowReleaseInfoChange", 0);
+ addArg(0, "allow-releaseinfo-change-origin", "Acquire::AllowReleaseInfoChange::Origin", 0);
+ addArg(0, "allow-releaseinfo-change-label", "Acquire::AllowReleaseInfoChange::Label", 0);
+ addArg(0, "allow-releaseinfo-change-version", "Acquire::AllowReleaseInfoChange::Version", 0);
+ addArg(0, "allow-releaseinfo-change-codename", "Acquire::AllowReleaseInfoChange::Codename", 0);
+ addArg(0, "allow-releaseinfo-change-suite", "Acquire::AllowReleaseInfoChange::Suite", 0);
+ addArg(0, "allow-releaseinfo-change-defaultpin", "Acquire::AllowReleaseInfoChange::DefaultPin", 0);
+ addArg('e', "error-on", "APT::Update::Error-Mode", CommandLine::HasArg);
+ }
+ else if (CmdMatches("source"))
+ {
+ addArg('b', "compile", "APT::Get::Compile", 0);
+ addArg('b', "build", "APT::Get::Compile", 0);
+ addArg('P', "build-profiles", "APT::Build-Profiles", CommandLine::HasArg);
+ addArg(0, "diff-only", "APT::Get::Diff-Only", 0);
+ addArg(0, "debian-only", "APT::Get::Diff-Only", 0);
+ addArg(0, "tar-only", "APT::Get::Tar-Only", 0);
+ addArg(0, "dsc-only", "APT::Get::Dsc-Only", 0);
+ }
+ else if (CmdMatches("build-dep") || CmdMatches("satisfy"))
+ {
+ addArg('a', "host-architecture", "APT::Get::Host-Architecture", CommandLine::HasArg);
+ addArg('P', "build-profiles", "APT::Build-Profiles", CommandLine::HasArg);
+ addArg(0, "purge", "APT::Get::Purge", 0);
+ addArg(0, "solver", "APT::Solver", CommandLine::HasArg);
+ if (CmdMatches("build-dep"))
+ {
+ addArg(0,"arch-only","APT::Get::Arch-Only",0);
+ addArg(0,"indep-only","APT::Get::Indep-Only",0);
+ }
+ // this has no effect *but* sbuild is using it (see LP: #1255806)
+ // once sbuild is fixed, this option can be removed
+ addArg('f', "fix-broken", "APT::Get::Fix-Broken", 0);
+ }
+ else if (CmdMatches("indextargets"))
+ {
+ addArg(0,"format","APT::Get::IndexTargets::Format", CommandLine::HasArg);
+ addArg(0,"release-info","APT::Get::IndexTargets::ReleaseInfo", 0);
+ }
+ else if (CmdMatches("clean", "autoclean", "auto-clean", "check", "download", "changelog") ||
+ CmdMatches("markauto", "unmarkauto")) // deprecated commands
+ ;
+ else if (CmdMatches("moo"))
+ addArg(0, "color", "APT::Moo::Color", 0);
+
+ if (CmdMatches("install", "reinstall", "remove", "purge", "upgrade", "dist-upgrade",
+ "dselect-upgrade", "autoremove", "auto-remove", "autopurge", "clean", "autoclean", "auto-clean", "check",
+ "build-dep", "satisfy", "full-upgrade", "source"))
+ {
+ addArg('s', "simulate", "APT::Get::Simulate", 0);
+ addArg('s', "just-print", "APT::Get::Simulate", 0);
+ addArg('s', "recon", "APT::Get::Simulate", 0);
+ addArg('s', "dry-run", "APT::Get::Simulate", 0);
+ addArg('s', "no-act", "APT::Get::Simulate", 0);
+ }
+
+ bool const found_something = Args.empty() == false;
+
+ // FIXME: move to the correct command(s)
+ addArg('d',"download-only","APT::Get::Download-Only",0);
+ addArg('y',"yes","APT::Get::Assume-Yes",0);
+ addArg('y',"assume-yes","APT::Get::Assume-Yes",0);
+ addArg(0,"assume-no","APT::Get::Assume-No",0);
+ addArg('u',"show-upgraded","APT::Get::Show-Upgraded",0);
+ addArg('m',"ignore-missing","APT::Get::Fix-Missing",0);
+ addArg('t',"target-release","APT::Default-Release",CommandLine::HasArg);
+ addArg('t',"default-release","APT::Default-Release",CommandLine::HasArg);
+ addArg(0,"download","APT::Get::Download",0);
+ addArg(0,"fix-missing","APT::Get::Fix-Missing",0);
+ addArg(0,"ignore-hold","APT::Ignore-Hold",0);
+ addArg(0,"upgrade","APT::Get::upgrade",0);
+ addArg(0,"only-upgrade","APT::Get::Only-Upgrade",0);
+ addArg(0,"allow-change-held-packages","APT::Get::allow-change-held-packages",CommandLine::Boolean);
+ addArg(0,"allow-remove-essential","APT::Get::allow-remove-essential",CommandLine::Boolean);
+ addArg(0,"allow-downgrades","APT::Get::allow-downgrades",CommandLine::Boolean);
+ addArg(0,"force-yes","APT::Get::force-yes",0);
+ addArg(0,"print-uris","APT::Get::Print-URIs",0);
+ addArg(0,"trivial-only","APT::Get::Trivial-Only",0);
+ addArg(0,"mark-auto","APT::Get::Mark-Auto",0);
+ addArg(0,"remove","APT::Get::Remove",0);
+ addArg(0,"only-source","APT::Get::Only-Source",0);
+ addArg(0,"allow-unauthenticated","APT::Get::AllowUnauthenticated",0);
+ addArg(0,"install-recommends","APT::Install-Recommends",CommandLine::Boolean);
+ addArg(0,"install-suggests","APT::Install-Suggests",CommandLine::Boolean);
+ addArg(0,"fix-policy","APT::Get::Fix-Policy-Broken",0);
+ addArg(0, "with-source", "APT::Sources::With::", CommandLine::HasArg);
+
+ return found_something;
+}
+ /*}}}*/
+static bool addArgumentsAPTMark(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("auto", "manual", "hold", "unhold", "showauto",
+ "showmanual", "showhold", "showholds", "showheld",
+ "markauto", "unmarkauto", "minimize-manual"))
+ {
+ addArg('f',"file","Dir::State::extended_states",CommandLine::HasArg);
+ }
+ else if (CmdMatches("install", "reinstall", "remove", "deinstall", "purge",
+ "showinstall", "showinstalls", "showremove", "showremoves",
+ "showdeinstall", "showdeinstalls", "showpurge", "showpurges"))
+ ;
+ else
+ return false;
+
+ if (CmdMatches("markauto", "unmarkauto"))
+ {
+ addArg('v',"verbose","APT::MarkAuto::Verbose",0);
+ }
+
+ if (CmdMatches("minimize-manual"))
+ {
+ addArg('y',"yes","APT::Get::Assume-Yes",0);
+ addArg('y',"assume-yes","APT::Get::Assume-Yes",0);
+ addArg(0,"assume-no","APT::Get::Assume-No",0);
+ }
+
+ if (CmdMatches("minimize-manual") || (Cmd != nullptr && strncmp(Cmd, "show", strlen("show")) != 0))
+ {
+ addArg('s',"simulate","APT::Mark::Simulate",0);
+ addArg('s',"just-print","APT::Mark::Simulate",0);
+ addArg('s',"recon","APT::Mark::Simulate",0);
+ addArg('s',"dry-run","APT::Mark::Simulate",0);
+ addArg('s',"no-act","APT::Mark::Simulate",0);
+ }
+ addArg(0, "with-source", "APT::Sources::With::", CommandLine::HasArg);
+
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPTSortPkgs(std::vector<CommandLine::Args> &Args, char const * const)/*{{{*/
+{
+ addArg('s',"source","APT::SortPkgs::Source",0);
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsAPT(std::vector<CommandLine::Args> &Args, char const * const Cmd)/*{{{*/
+{
+ if (CmdMatches("list"))
+ {
+ addArg('i',"installed","APT::Cmd::Installed",0);
+ addArg(0,"upgradeable","APT::Cmd::Upgradable",0);
+ addArg('u',"upgradable","APT::Cmd::Upgradable",0);
+ addArg(0,"manual-installed","APT::Cmd::Manual-Installed",0);
+ addArg('v', "verbose", "APT::Cmd::List-Include-Summary", 0);
+ addArg('a', "all-versions", "APT::Cmd::All-Versions", 0);
+ }
+ else if (CmdMatches("show") || CmdMatches("info"))
+ {
+ addArg('a', "all-versions", "APT::Cache::AllVersions", 0);
+ }
+ else if (addArgumentsAPTGet(Args, Cmd) || addArgumentsAPTCache(Args, Cmd))
+ {
+ // we have no (supported) command-name overlaps so far, so we call
+ // specifics in order until we find one which adds arguments
+ }
+ else
+ return false;
+
+ addArg(0, "with-source", "APT::Sources::With::", CommandLine::HasArg);
+
+ return true;
+}
+ /*}}}*/
+static bool addArgumentsRred(std::vector<CommandLine::Args> &Args, char const * const /*Cmd*/)/*{{{*/
+{
+ addArg('t', nullptr, "Rred::T", 0);
+ addArg('f', nullptr, "Rred::F", 0);
+ addArg('C', "compress", "Rred::Compress",CommandLine::HasArg);
+ return true;
+}
+ /*}}}*/
+std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const * const Cmd)/*{{{*/
+{
+ std::vector<CommandLine::Args> Args;
+ Args.reserve(50);
+ if (Cmd != nullptr && strcmp(Cmd, "help") == 0)
+ ; // no options for help so no need to implement it in each
+ else
+ switch (Program)
+ {
+ case APT_CMD::APT: addArgumentsAPT(Args, Cmd); break;
+ case APT_CMD::APT_GET: addArgumentsAPTGet(Args, Cmd); break;
+ case APT_CMD::APT_CACHE: addArgumentsAPTCache(Args, Cmd); break;
+ case APT_CMD::APT_CDROM: addArgumentsAPTCDROM(Args, Cmd); break;
+ case APT_CMD::APT_CONFIG: addArgumentsAPTConfig(Args, Cmd); break;
+ case APT_CMD::APT_DUMP_SOLVER: addArgumentsAPTDumpSolver(Args, Cmd); break;
+ case APT_CMD::APT_EXTRACTTEMPLATES: addArgumentsAPTExtractTemplates(Args, Cmd); break;
+ case APT_CMD::APT_FTPARCHIVE: addArgumentsAPTFTPArchive(Args, Cmd); break;
+ case APT_CMD::APT_HELPER: addArgumentsAPTHelper(Args, Cmd); break;
+ case APT_CMD::APT_INTERNAL_PLANNER: addArgumentsAPTInternalPlanner(Args, Cmd); break;
+ case APT_CMD::APT_INTERNAL_SOLVER: addArgumentsAPTInternalSolver(Args, Cmd); break;
+ case APT_CMD::APT_MARK: addArgumentsAPTMark(Args, Cmd); break;
+ case APT_CMD::APT_SORTPKG: addArgumentsAPTSortPkgs(Args, Cmd); break;
+ case APT_CMD::RRED: addArgumentsRred(Args, Cmd); break;
+ }
+
+ // options without a command
+ addArg('h', "help", "help", 0);
+ addArg('v', "version", "version", 0);
+ // general options
+ addArg('q', "quiet", "quiet", CommandLine::IntLevel);
+ addArg('q', "silent", "quiet", CommandLine::IntLevel);
+ addArg('c', "config-file", 0, CommandLine::ConfigFile);
+ addArg('o', "option", 0, CommandLine::ArbItem);
+ addArg(0, NULL, NULL, 0);
+
+ return Args;
+}
+ /*}}}*/
+#undef addArg
+static void ShowHelpListCommands(std::vector<aptDispatchWithHelp> const &Cmds)/*{{{*/
+{
+ if (Cmds.empty() || Cmds[0].Match == nullptr)
+ return;
+ std::cout << std::endl << _("Most used commands:") << std::endl;
+ for (auto const &c: Cmds)
+ {
+ if (c.Help == nullptr)
+ continue;
+ std::cout << " " << c.Match << " - " << c.Help << std::endl;
+ }
+}
+ /*}}}*/
+static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector<aptDispatchWithHelp> const &Cmds,/*{{{*/
+ bool (*ShowHelp)(CommandLine &))
+{
+ std::cout << PACKAGE << " " << PACKAGE_VERSION << " (" << COMMON_ARCH << ")" << std::endl;
+ if (_config->FindB("version") == true && Binary != APT_CMD::APT_GET)
+ return true;
+ if (ShowHelp(CmdL) == false)
+ return false;
+ if (_config->FindB("version") == true || Binary == APT_CMD::APT_FTPARCHIVE)
+ return true;
+ ShowHelpListCommands(Cmds);
+ std::cout << std::endl;
+ char const * cmd = nullptr;
+ switch (Binary)
+ {
+ case APT_CMD::APT: cmd = "apt(8)"; break;
+ case APT_CMD::APT_CACHE: cmd = "apt-cache(8)"; break;
+ case APT_CMD::APT_CDROM: cmd = "apt-cdrom(8)"; break;
+ case APT_CMD::APT_CONFIG: cmd = "apt-config(8)"; break;
+ case APT_CMD::APT_DUMP_SOLVER: cmd = nullptr; break;
+ case APT_CMD::APT_EXTRACTTEMPLATES: cmd = "apt-extracttemplates(1)"; break;
+ case APT_CMD::APT_FTPARCHIVE: cmd = "apt-ftparchive(1)"; break;
+ case APT_CMD::APT_GET: cmd = "apt-get(8)"; break;
+ case APT_CMD::APT_HELPER: cmd = nullptr; break;
+ case APT_CMD::APT_INTERNAL_PLANNER: cmd = nullptr; break;
+ case APT_CMD::APT_INTERNAL_SOLVER: cmd = nullptr; break;
+ case APT_CMD::APT_MARK: cmd = "apt-mark(8)"; break;
+ case APT_CMD::APT_SORTPKG: cmd = "apt-sortpkgs(1)"; break;
+ case APT_CMD::RRED: cmd = nullptr; break;
+ }
+ if (cmd != nullptr)
+ ioprintf(std::cout, _("See %s for more information about the available commands."), cmd);
+ if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER &&
+ Binary != APT_CMD::APT_INTERNAL_PLANNER && Binary != APT_CMD::RRED)
+ std::cout << std::endl <<
+ _("Configuration options and syntax is detailed in apt.conf(5).\n"
+ "Information about how to configure sources can be found in sources.list(5).\n"
+ "Package and version choices can be expressed via apt_preferences(5).\n"
+ "Security details are available in apt-secure(8).\n");
+ if (Binary == APT_CMD::APT_GET || Binary == APT_CMD::APT)
+ std::cout << std::right << std::setw(70) << _("This APT has Super Cow Powers.") << std::endl;
+ else if (Binary == APT_CMD::APT_HELPER || Binary == APT_CMD::APT_DUMP_SOLVER)
+ std::cout << std::right << std::setw(70) << _("This APT helper has Super Meep Powers.") << std::endl;
+ return true;
+}
+ /*}}}*/
+static void BinarySpecificConfiguration(char const * const Binary) /*{{{*/
+{
+ std::string const binary = flNotDir(Binary);
+ if (binary == "apt" || binary == "apt-config")
+ {
+ _config->CndSet("Binary::apt::APT::Color", true);
+ _config->CndSet("Binary::apt::APT::Cache::Show::Version", 2);
+ _config->CndSet("Binary::apt::APT::Cache::AllVersions", false);
+ _config->CndSet("Binary::apt::APT::Cache::ShowVirtuals", true);
+ _config->CndSet("Binary::apt::APT::Cache::Search::Version", 2);
+ _config->CndSet("Binary::apt::APT::Cache::ShowDependencyType", true);
+ _config->CndSet("Binary::apt::APT::Cache::ShowVersion", true);
+ _config->CndSet("Binary::apt::APT::Get::Upgrade-Allow-New", true);
+ _config->CndSet("Binary::apt::APT::Cmd::Show-Update-Stats", true);
+ _config->CndSet("Binary::apt::DPkg::Progress-Fancy", true);
+ _config->CndSet("Binary::apt::APT::Keep-Downloaded-Packages", false);
+ _config->CndSet("Binary::apt::APT::Get::Update::InteractiveReleaseInfoChanges", true);
+ _config->CndSet("Binary::apt::APT::Cmd::Pattern-Only", true);
+
+ if (isatty(STDIN_FILENO))
+ _config->CndSet("Binary::apt::Dpkg::Lock::Timeout", -1);
+ else
+ _config->CndSet("Binary::apt::Dpkg::Lock::Timeout", 120);
+ }
+
+ _config->Set("Binary", binary);
+}
+ /*}}}*/
+static void BinaryCommandSpecificConfiguration(char const * const Binary, char const * const Cmd)/*{{{*/
+{
+ std::string const binary = flNotDir(Binary);
+ if ((binary == "apt" || binary == "apt-get") && CmdMatches("upgrade", "dist-upgrade", "full-upgrade"))
+ {
+ //FIXME: the option is documented to apply only for install/remove, so
+ // we force it false for configuration files where users can be confused if
+ // we support it anyhow, but allow it on the commandline to take effect
+ // even through it isn't documented as a user who doesn't want it wouldn't
+ // ask for it
+ _config->Set("APT::Get::AutomaticRemove", "");
+ }
+}
+#undef CmdMatches
+ /*}}}*/
+std::vector<CommandLine::Dispatch> ParseCommandLine(CommandLine &CmdL, APT_CMD const Binary,/*{{{*/
+ Configuration * const * const Cnf, pkgSystem ** const Sys, int const argc, const char *argv[],
+ bool (*ShowHelp)(CommandLine &), std::vector<aptDispatchWithHelp> (*GetCommands)(void))
+{
+ InitLocale(Binary);
+ if (Cnf != NULL && pkgInitConfig(**Cnf) == false)
+ {
+ _error->DumpErrors();
+ exit(100);
+ }
+
+ if (likely(argc != 0 && argv[0] != NULL))
+ BinarySpecificConfiguration(argv[0]);
+
+ std::vector<CommandLine::Dispatch> Cmds;
+ std::vector<aptDispatchWithHelp> const CmdsWithHelp = GetCommands();
+ if (CmdsWithHelp.empty() == false)
+ {
+ CommandLine::Dispatch const help = { "help", [](CommandLine &){return false;} };
+ Cmds.push_back(std::move(help));
+ }
+ std::transform(CmdsWithHelp.begin(), CmdsWithHelp.end(), std::back_inserter(Cmds),
+ [](auto &&cmd) { return CommandLine::Dispatch{cmd.Match, cmd.Handler}; });
+
+ char const * CmdCalled = nullptr;
+ if (Cmds.empty() == false && Cmds[0].Handler != nullptr)
+ CmdCalled = CommandLine::GetCommand(Cmds.data(), argc, argv);
+ if (CmdCalled != nullptr)
+ BinaryCommandSpecificConfiguration(argv[0], CmdCalled);
+ std::string const conf = "Binary::" + _config->Find("Binary");
+ _config->MoveSubTree(conf.c_str(), nullptr);
+
+ // Args running out of scope invalidates the pointer stored in CmdL,
+ // but we don't use the pointer after this function, so we ignore
+ // this problem for now and figure something out if we have to.
+ auto Args = getCommandArgs(Binary, CmdCalled);
+ CmdL = CommandLine(Args.data(), _config);
+
+ if (CmdL.Parse(argc,argv) == false ||
+ (Sys != NULL && pkgInitSystem(*_config, *Sys) == false))
+ {
+ if (_config->FindB("version") == true)
+ ShowCommonHelp(Binary, CmdL, CmdsWithHelp, ShowHelp);
+
+ _error->DumpErrors();
+ exit(100);
+ }
+
+ if (_config->FindB("APT::Get::Force-Yes", false) == true)
+ {
+ _error->Warning(_("--force-yes is deprecated, use one of the options starting with --allow instead."));
+ }
+
+ // See if the help should be shown
+ if (_config->FindB("help") == true || _config->FindB("version") == true ||
+ (CmdL.FileSize() > 0 && strcmp(CmdL.FileList[0], "help") == 0))
+ {
+ ShowCommonHelp(Binary, CmdL, CmdsWithHelp, ShowHelp);
+ exit(0);
+ }
+ if (Cmds.empty() == false && CmdL.FileSize() == 0)
+ {
+ ShowCommonHelp(Binary, CmdL, CmdsWithHelp, ShowHelp);
+ exit(1);
+ }
+ return Cmds;
+}
+ /*}}}*/
+unsigned short DispatchCommandLine(CommandLine &CmdL, std::vector<CommandLine::Dispatch> const &Cmds) /*{{{*/
+{
+ // Match the operation
+ bool const returned = Cmds.empty() ? true : CmdL.DispatchArg(Cmds.data());
+
+ // Print any errors or warnings found during parsing
+ bool const Errors = _error->PendingError();
+ if (_config->FindI("quiet",0) > 0)
+ _error->DumpErrors();
+ else
+ _error->DumpErrors(GlobalError::DEBUG);
+ if (returned == false)
+ return 100;
+ return Errors == true ? 100 : 0;
+}
+ /*}}}*/
diff --git a/apt-private/private-cmndline.h b/apt-private/private-cmndline.h
new file mode 100644
index 0000000..22e25d2
--- /dev/null
+++ b/apt-private/private-cmndline.h
@@ -0,0 +1,42 @@
+#ifndef APT_PRIVATE_CMNDLINE_H
+#define APT_PRIVATE_CMNDLINE_H
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/macros.h>
+
+#include <vector>
+
+class Configuration;
+class pkgSystem;
+
+enum class APT_CMD {
+ APT,
+ APT_GET,
+ APT_CACHE,
+ APT_CDROM,
+ APT_CONFIG,
+ APT_EXTRACTTEMPLATES,
+ APT_FTPARCHIVE,
+ APT_HELPER,
+ APT_INTERNAL_SOLVER,
+ APT_MARK,
+ APT_SORTPKG,
+ APT_DUMP_SOLVER,
+ APT_INTERNAL_PLANNER,
+ RRED,
+};
+struct aptDispatchWithHelp
+{
+ const char *Match;
+ bool (*Handler)(CommandLine &);
+ const char *Help;
+};
+
+APT_PUBLIC std::vector<CommandLine::Dispatch> ParseCommandLine(CommandLine &CmdL, APT_CMD const Binary,
+ Configuration * const * const Cnf, pkgSystem ** const Sys, int const argc, const char * argv[],
+ bool (*ShowHelp)(CommandLine &), std::vector<aptDispatchWithHelp> (*GetCommands)(void));
+APT_PUBLIC unsigned short DispatchCommandLine(CommandLine &CmdL, std::vector<CommandLine::Dispatch> const &Cmds);
+
+APT_PUBLIC std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const * const Cmd);
+
+#endif
diff --git a/apt-private/private-depends.cc b/apt-private/private-depends.cc
new file mode 100644
index 0000000..95c747e
--- /dev/null
+++ b/apt-private/private-depends.cc
@@ -0,0 +1,151 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/pkgcache.h>
+
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-depends.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <stddef.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// ShowDepends - Helper for printing out a dependency tree /*{{{*/
+static bool ShowDepends(CommandLine &CmdL, bool const RevDepends)
+{
+ pkgCacheFile CacheFile;
+ pkgCache * const Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == nullptr || CacheFile.GetDepCache() == nullptr))
+ return false;
+
+ CacheSetHelperVirtuals helper(false);
+ APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::CacheSetHelper::CANDIDATE, helper);
+ if (verset.empty() == true && helper.virtualPkgs.empty() == true)
+ return _error->Error(_("No packages found"));
+ std::vector<bool> Shown(Cache->Head().PackageCount);
+
+ bool const Recurse = _config->FindB("APT::Cache::RecurseDepends", false);
+ bool const Installed = _config->FindB("APT::Cache::Installed", false);
+ bool const Important = _config->FindB("APT::Cache::Important", false);
+ bool const ShowDepType = _config->FindB("APT::Cache::ShowDependencyType", RevDepends == false);
+ bool const ShowVersion = _config->FindB("APT::Cache::ShowVersion", false);
+ bool const ShowPreDepends = _config->FindB("APT::Cache::ShowPre-Depends", true);
+ bool const ShowDepends = _config->FindB("APT::Cache::ShowDepends", true);
+ bool const ShowRecommends = _config->FindB("APT::Cache::ShowRecommends", Important == false);
+ bool const ShowSuggests = _config->FindB("APT::Cache::ShowSuggests", Important == false);
+ bool const ShowReplaces = _config->FindB("APT::Cache::ShowReplaces", Important == false);
+ bool const ShowConflicts = _config->FindB("APT::Cache::ShowConflicts", Important == false);
+ bool const ShowBreaks = _config->FindB("APT::Cache::ShowBreaks", Important == false);
+ bool const ShowEnhances = _config->FindB("APT::Cache::ShowEnhances", Important == false);
+ bool const ShowOnlyFirstOr = _config->FindB("APT::Cache::ShowOnlyFirstOr", false);
+ bool const ShowImplicit = _config->FindB("APT::Cache::ShowImplicit", false);
+
+ while (verset.empty() != true)
+ {
+ pkgCache::VerIterator Ver = *verset.begin();
+ verset.erase(verset.begin());
+ pkgCache::PkgIterator Pkg = Ver.ParentPkg();
+ Shown[Pkg->ID] = true;
+
+ std::cout << Pkg.FullName(true) << std::endl;
+
+ if (RevDepends == true)
+ std::cout << "Reverse Depends:" << std::endl;
+ for (pkgCache::DepIterator D = RevDepends ? Pkg.RevDependsList() : Ver.DependsList();
+ D.end() == false; ++D)
+ {
+ switch (D->Type) {
+ case pkgCache::Dep::PreDepends: if (!ShowPreDepends) continue; break;
+ case pkgCache::Dep::Depends: if (!ShowDepends) continue; break;
+ case pkgCache::Dep::Recommends: if (!ShowRecommends) continue; break;
+ case pkgCache::Dep::Suggests: if (!ShowSuggests) continue; break;
+ case pkgCache::Dep::Replaces: if (!ShowReplaces) continue; break;
+ case pkgCache::Dep::Conflicts: if (!ShowConflicts) continue; break;
+ case pkgCache::Dep::DpkgBreaks: if (!ShowBreaks) continue; break;
+ case pkgCache::Dep::Enhances: if (!ShowEnhances) continue; break;
+ }
+ if (ShowImplicit == false && D.IsImplicit())
+ continue;
+
+ pkgCache::PkgIterator Trg = RevDepends ? D.ParentPkg() : D.TargetPkg();
+
+ if((Installed && Trg->CurrentVer != 0) || !Installed)
+ {
+
+ if ((D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or && ShowOnlyFirstOr == false)
+ std::cout << " |";
+ else
+ std::cout << " ";
+
+ // Show the package
+ if (ShowDepType == true)
+ std::cout << D.DepType() << ": ";
+ if (Trg->VersionList == 0)
+ std::cout << "<" << Trg.FullName(true) << ">";
+ else
+ std::cout << Trg.FullName(true);
+ if (ShowVersion == true && D->Version != 0)
+ std::cout << " (" << pkgCache::CompTypeDeb(D->CompareOp) << ' ' << D.TargetVer() << ')';
+ std::cout << std::endl;
+
+ if (Recurse == true && Shown[Trg->ID] == false)
+ {
+ Shown[Trg->ID] = true;
+ verset.insert(APT::VersionSet::FromPackage(CacheFile, Trg, APT::CacheSetHelper::CANDIDATE, helper));
+ }
+
+ // Display all solutions
+ std::unique_ptr<pkgCache::Version *[]> List(D.AllTargets());
+ pkgPrioSortList(*Cache,List.get());
+ for (pkgCache::Version **I = List.get(); *I != 0; I++)
+ {
+ pkgCache::VerIterator V(*Cache,*I);
+ if (V != Cache->VerP + V.ParentPkg()->VersionList ||
+ V->ParentPkg == D->Package)
+ continue;
+ std::cout << " " << V.ParentPkg().FullName(true) << std::endl;
+
+ if (Recurse == true && Shown[V.ParentPkg()->ID] == false)
+ {
+ Shown[V.ParentPkg()->ID] = true;
+ verset.insert(APT::VersionSet::FromPackage(CacheFile, V.ParentPkg(), APT::CacheSetHelper::CANDIDATE, helper));
+ }
+ }
+
+ }
+
+ if (ShowOnlyFirstOr == true)
+ while ((D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or) ++D;
+ }
+ }
+
+ for (APT::PackageSet::const_iterator Pkg = helper.virtualPkgs.begin();
+ Pkg != helper.virtualPkgs.end(); ++Pkg)
+ std::cout << '<' << Pkg.FullName(true) << '>' << std::endl;
+
+ return true;
+}
+ /*}}}*/
+// Depends - Print out a dependency tree /*{{{*/
+bool Depends(CommandLine &CmdL)
+{
+ return ShowDepends(CmdL, false);
+}
+ /*}}}*/
+// RDepends - Print out a reverse dependency tree /*{{{*/
+bool RDepends(CommandLine &CmdL)
+{
+ return ShowDepends(CmdL, true);
+}
+ /*}}}*/
diff --git a/apt-private/private-depends.h b/apt-private/private-depends.h
new file mode 100644
index 0000000..e9f703d
--- /dev/null
+++ b/apt-private/private-depends.h
@@ -0,0 +1,11 @@
+#ifndef APT_PRIVATE_DEPENDS_H
+#define APT_PRIVATE_DEPENDS_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool Depends(CommandLine &CmdL);
+APT_PUBLIC bool RDepends(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-download.cc b/apt-private/private-download.cc
new file mode 100644
index 0000000..16d1125
--- /dev/null
+++ b/apt-private/private-download.cc
@@ -0,0 +1,378 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/clean.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/acqprogress.h>
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-utils.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_VFS_H
+#include <sys/vfs.h>
+#else
+#ifdef HAVE_PARAMS_H
+#include <sys/params.h>
+#endif
+#include <sys/mount.h>
+#endif
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// CheckAuth - check if each download comes form a trusted source /*{{{*/
+bool CheckAuth(pkgAcquire& Fetcher, bool const PromptUser)
+{
+ std::vector<std::string> UntrustedList;
+ for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I < Fetcher.ItemsEnd(); ++I)
+ if (!(*I)->IsTrusted())
+ UntrustedList.push_back((*I)->ShortDesc());
+
+ if (UntrustedList.empty())
+ return true;
+
+ return AuthPrompt(UntrustedList, PromptUser);
+}
+ /*}}}*/
+bool AuthPrompt(std::vector<std::string> const &UntrustedList, bool const PromptUser)/*{{{*/
+{
+ ShowList(c2out,_("WARNING: The following packages cannot be authenticated!"), UntrustedList,
+ [](std::string const&) { return true; },
+ [](std::string const&str) { return str; },
+ [](std::string const&) { return ""; });
+
+ if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
+ {
+ c2out << _("Authentication warning overridden.\n");
+ return true;
+ }
+
+ if (PromptUser == false)
+ return _error->Error(_("Some packages could not be authenticated"));
+
+ if (_config->FindI("quiet",0) < 2
+ && _config->FindB("APT::Get::Assume-Yes",false) == false)
+ {
+ if (!YnPrompt(_("Install these packages without verification?"), false))
+ return _error->Error(_("Some packages could not be authenticated"));
+
+ return true;
+ }
+ else if (_config->FindB("APT::Get::Force-Yes",false) == true) {
+ return true;
+ }
+
+ return _error->Error(_("There were unauthenticated packages and -y was used without --allow-unauthenticated"));
+}
+ /*}}}*/
+bool AcquireRun(pkgAcquire &Fetcher, int const PulseInterval, bool * const Failure, bool * const TransientNetworkFailure)/*{{{*/
+{
+ pkgAcquire::RunResult res;
+ if(PulseInterval > 0)
+ res = Fetcher.Run(PulseInterval);
+ else
+ res = Fetcher.Run();
+
+ if (res == pkgAcquire::Failed)
+ return false;
+
+ for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin();
+ I != Fetcher.ItemsEnd(); ++I)
+ {
+
+ if ((*I)->Status == pkgAcquire::Item::StatDone &&
+ (*I)->Complete == true)
+ continue;
+
+ if (TransientNetworkFailure != NULL && (*I)->Status == pkgAcquire::Item::StatIdle)
+ {
+ *TransientNetworkFailure = true;
+ continue;
+ }
+
+ ::URI uri((*I)->DescURI());
+ uri.User.clear();
+ uri.Password.clear();
+ std::string descUri = std::string(uri);
+ _error->Error(_("Failed to fetch %s %s"), descUri.c_str(),
+ (*I)->ErrorText.c_str());
+
+ if (Failure != NULL)
+ *Failure = true;
+ }
+
+ return true;
+}
+ /*}}}*/
+bool CheckFreeSpaceBeforeDownload(std::string const &Dir, unsigned long long FetchBytes)/*{{{*/
+{
+ uint32_t const RAMFS_MAGIC = 0x858458f6;
+ /* Check for enough free space, but only if we are actually going to
+ download */
+ if (_config->FindB("APT::Get::Print-URIs", false) == true ||
+ _config->FindB("APT::Get::Download", true) == false)
+ return true;
+
+ struct statvfs Buf;
+ if (statvfs(Dir.c_str(),&Buf) != 0) {
+ if (errno == EOVERFLOW)
+ return _error->WarningE("statvfs",_("Couldn't determine free space in %s"),
+ Dir.c_str());
+ else
+ return _error->Errno("statvfs",_("Couldn't determine free space in %s"),
+ Dir.c_str());
+ }
+ else
+ {
+ unsigned long long const FreeBlocks = _config->Find("APT::Sandbox::User").empty() ? Buf.f_bfree : Buf.f_bavail;
+ if (FreeBlocks < (FetchBytes / Buf.f_bsize))
+ {
+ struct statfs Stat;
+ if (statfs(Dir.c_str(),&Stat) != 0
+#ifdef HAVE_STRUCT_STATFS_F_TYPE
+ || Stat.f_type != RAMFS_MAGIC
+#endif
+ )
+ return _error->Error(_("You don't have enough free space in %s."),
+ Dir.c_str());
+ }
+ }
+ return true;
+}
+ /*}}}*/
+
+aptAcquireWithTextStatus::aptAcquireWithTextStatus() : pkgAcquire::pkgAcquire(),
+ Stat(std::cout, ScreenWidth, _config->FindI("quiet",0))
+{
+ SetLog(&Stat);
+}
+
+// DoDownload - download a binary /*{{{*/
+bool DoDownload(CommandLine &CmdL)
+{
+ CacheFile Cache;
+ if (Cache.ReadOnlyOpen() == false)
+ return false;
+
+ APT::CacheSetHelper helper;
+ APT::VersionSet verset = APT::VersionSet::FromCommandLine(Cache,
+ CmdL.FileList + 1, APT::CacheSetHelper::CANDIDATE, helper);
+
+ if (verset.empty() == true)
+ return false;
+
+ pkgRecords Recs(Cache);
+ pkgSourceList *SrcList = Cache.GetSourceList();
+
+ // reuse the usual acquire methods for deb files, but don't drop them into
+ // the usual directories - keep everything in the current directory
+ aptAcquireWithTextStatus Fetcher;
+ std::vector<std::string> storefile(verset.size());
+ std::string const cwd = SafeGetCWD();
+ _config->Set("Dir::Cache::Archives", cwd);
+ int i = 0;
+ for (APT::VersionSet::const_iterator Ver = verset.begin();
+ Ver != verset.end(); ++Ver, ++i)
+ {
+ pkgAcquire::Item *I = new pkgAcqArchive(&Fetcher, SrcList, &Recs, *Ver, storefile[i]);
+ if (storefile[i].empty())
+ continue;
+ std::string const filename = cwd + flNotDir(storefile[i]);
+ storefile[i].assign(filename);
+ I->DestFile.assign(filename);
+ }
+
+ // Just print out the uris and exit if the --print-uris flag was used
+ if (_config->FindB("APT::Get::Print-URIs") == true)
+ {
+ pkgAcquire::UriIterator I = Fetcher.UriBegin();
+ for (; I != Fetcher.UriEnd(); ++I)
+ std::cout << '\'' << I->URI << "' " << flNotDir(I->Owner->DestFile) << ' ' <<
+ I->Owner->FileSize << ' ' << I->Owner->HashSum() << std::endl;
+ return true;
+ }
+
+ if (_error->PendingError() == true || CheckAuth(Fetcher, false) == false)
+ return false;
+
+ bool Failed = false;
+ if (AcquireRun(Fetcher, 0, &Failed, NULL) == false)
+ return false;
+
+ // copy files in local sources to the current directory
+ for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I != Fetcher.ItemsEnd(); ++I)
+ {
+ std::string const filename = cwd + flNotDir((*I)->DestFile);
+ if ((*I)->Local == true &&
+ filename != (*I)->DestFile &&
+ (*I)->Status == pkgAcquire::Item::StatDone &&
+ dynamic_cast<pkgAcqArchive*>(*I) != nullptr)
+ {
+ std::ifstream src((*I)->DestFile.c_str(), std::ios::binary);
+ std::ofstream dst(filename.c_str(), std::ios::binary);
+ dst << src.rdbuf();
+ chmod(filename.c_str(), 0644);
+ }
+ }
+ return Failed == false;
+}
+ /*}}}*/
+// DoChangelog - Get changelog from the command line /*{{{*/
+bool DoChangelog(CommandLine &CmdL)
+{
+ CacheFile Cache;
+ if (Cache.ReadOnlyOpen() == false)
+ return false;
+
+ APT::CacheSetHelper helper;
+ APT::VersionList verset = APT::VersionList::FromCommandLine(Cache,
+ CmdL.FileList + 1, APT::CacheSetHelper::CANDIDATE, helper);
+ if (verset.empty() == true)
+ return _error->Error(_("No packages found"));
+
+ bool const downOnly = _config->FindB("APT::Get::Download-Only", false);
+ bool const printOnly = _config->FindB("APT::Get::Print-URIs", false);
+ if (printOnly)
+ _config->CndSet("Acquire::Changelogs::AlwaysOnline", true);
+
+ aptAcquireWithTextStatus Fetcher;
+ for (APT::VersionList::const_iterator Ver = verset.begin();
+ Ver != verset.end();
+ ++Ver)
+ {
+ if (printOnly)
+ new pkgAcqChangelog(&Fetcher, Ver, "/dev/null");
+ else if (downOnly)
+ new pkgAcqChangelog(&Fetcher, Ver, ".");
+ else
+ new pkgAcqChangelog(&Fetcher, Ver);
+ }
+
+ if (printOnly == false)
+ {
+ bool Failed = false;
+ if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == true)
+ return false;
+ }
+
+ if (downOnly == false || printOnly == true)
+ {
+ bool Failed = false;
+ for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I != Fetcher.ItemsEnd(); ++I)
+ {
+ if (printOnly)
+ {
+ if ((*I)->ErrorText.empty() == false)
+ {
+ Failed = true;
+ _error->Error("%s", (*I)->ErrorText.c_str());
+ }
+ else
+ std::cout << '\'' << (*I)->DescURI() << "' " << flNotDir((*I)->DestFile) << std::endl;
+ }
+ else
+ DisplayFileInPager((*I)->DestFile);
+ }
+ return Failed == false;
+ }
+
+ return true;
+}
+ /*}}}*/
+
+// DoClean - Remove download archives /*{{{*/
+bool DoClean(CommandLine &)
+{
+ std::string const archivedir = _config->FindDir("Dir::Cache::archives");
+ std::string const listsdir = _config->FindDir("Dir::state::lists");
+
+ if (_config->FindB("APT::Get::Simulate") == true)
+ {
+ std::string const pkgcache = _config->FindFile("Dir::cache::pkgcache");
+ std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
+ std::cout << "Del " << archivedir << "* " << archivedir << "partial/*"<< std::endl
+ << "Del " << listsdir << "partial/*" << std::endl
+ << "Del " << pkgcache << " " << srcpkgcache << std::endl;
+ return true;
+ }
+
+ pkgAcquire Fetcher;
+ if (archivedir.empty() == false && FileExists(archivedir) == true &&
+ Fetcher.GetLock(archivedir) == true)
+ {
+ Fetcher.Clean(archivedir);
+ Fetcher.Clean(archivedir + "partial/");
+ }
+
+ if (listsdir.empty() == false && FileExists(listsdir) == true &&
+ Fetcher.GetLock(listsdir) == true)
+ {
+ Fetcher.Clean(listsdir + "partial/");
+ }
+
+ pkgCacheFile::RemoveCaches();
+
+ return true;
+}
+ /*}}}*/
+// DoAutoClean - Smartly remove downloaded archives /*{{{*/
+// ---------------------------------------------------------------------
+/* This is similar to clean but it only purges things that cannot be
+ downloaded, that is old versions of cached packages. */
+ class LogCleaner : public pkgArchiveCleaner
+{
+ protected:
+ virtual void Erase(int const dirfd, char const * const File, std::string const &Pkg, std::string const &Ver,struct stat const &St) APT_OVERRIDE
+ {
+ c1out << "Del " << Pkg << " " << Ver << " [" << SizeToStr(St.st_size) << "B]" << std::endl;
+
+ if (_config->FindB("APT::Get::Simulate") == false)
+ RemoveFileAt("Cleaner::Erase", dirfd, File);
+ };
+};
+bool DoAutoClean(CommandLine &)
+{
+ std::string const archivedir = _config->FindDir("Dir::Cache::Archives");
+ if (FileExists(archivedir) == false)
+ return true;
+
+ // Lock the archive directory
+ FileFd Lock;
+ if (_config->FindB("Debug::NoLocking",false) == false)
+ {
+ int lock_fd = GetLock(flCombine(archivedir, "lock"));
+ if (lock_fd < 0)
+ return _error->Error(_("Unable to lock the download directory"));
+ Lock.Fd(lock_fd);
+ }
+
+ CacheFile Cache;
+ if (Cache.Open(false) == false)
+ return false;
+
+ LogCleaner Cleaner;
+
+ return Cleaner.Go(archivedir, *Cache) &&
+ Cleaner.Go(flCombine(archivedir, "partial/"), *Cache);
+}
+ /*}}}*/
diff --git a/apt-private/private-download.h b/apt-private/private-download.h
new file mode 100644
index 0000000..d829e8b
--- /dev/null
+++ b/apt-private/private-download.h
@@ -0,0 +1,38 @@
+#ifndef APT_PRIVATE_DOWNLOAD_H
+#define APT_PRIVATE_DOWNLOAD_H
+
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/macros.h>
+
+#include <apt-private/acqprogress.h>
+
+#include <string>
+#include <vector>
+
+// Check if all files in the fetcher are authenticated
+bool CheckAuth(pkgAcquire& Fetcher, bool const PromptUser);
+
+// show a authentication warning prompt and return true if the system
+// should continue
+bool AuthPrompt(std::vector<std::string> const &UntrustedList, bool const PromptUser);
+
+APT_PUBLIC bool AcquireRun(pkgAcquire &Fetcher, int const PulseInterval, bool * const Failure, bool * const TransientNetworkFailure);
+
+bool CheckFreeSpaceBeforeDownload(std::string const &Dir, unsigned long long FetchBytes);
+
+class APT_PUBLIC aptAcquireWithTextStatus : public pkgAcquire
+{
+ AcqTextStatus Stat;
+public:
+ aptAcquireWithTextStatus();
+};
+
+class CommandLine;
+
+APT_PUBLIC bool DoDownload(CommandLine &CmdL);
+APT_PUBLIC bool DoChangelog(CommandLine &CmdL);
+
+APT_PUBLIC bool DoClean(CommandLine &CmdL);
+APT_PUBLIC bool DoAutoClean(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc
new file mode 100644
index 0000000..402f8f4
--- /dev/null
+++ b/apt-private/private-install.cc
@@ -0,0 +1,1118 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/install-progress.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/packagemanager.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/prettyprinters.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/upgrade.h>
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <set>
+#include <vector>
+#include <stdlib.h>
+#include <string.h>
+
+#include <apt-private/acqprogress.h>
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-install.h>
+#include <apt-private/private-json-hooks.h>
+#include <apt-private/private-output.h>
+
+#include <apti18n.h>
+ /*}}}*/
+class pkgSourceList;
+
+bool CheckNothingBroken(CacheFile &Cache) /*{{{*/
+{
+ // Now we check the state of the packages,
+ if (Cache->BrokenCount() == 0)
+ return true;
+
+ // FIXME: if an external solver showed an error, we shouldn't show one here
+ if (_error->PendingError() && _config->Find("APT::Solver") == "dump")
+ return false;
+
+ c1out <<
+ _("Some packages could not be installed. This may mean that you have\n"
+ "requested an impossible situation or if you are using the unstable\n"
+ "distribution that some required packages have not yet been created\n"
+ "or been moved out of Incoming.") << std::endl;
+ /*
+ if (Packages == 1)
+ {
+ c1out << std::endl;
+ c1out <<
+ _("Since you only requested a single operation it is extremely likely that\n"
+ "the package is simply not installable and a bug report against\n"
+ "that package should be filed.") << std::endl;
+ }
+ */
+
+ c1out << _("The following information may help to resolve the situation:") << std::endl;
+ c1out << std::endl;
+ ShowBroken(c1out,Cache,false);
+ if (_error->PendingError() == true)
+ return false;
+ else
+ return _error->Error(_("Broken packages"));
+}
+ /*}}}*/
+// InstallPackages - Actually download and install the packages /*{{{*/
+// ---------------------------------------------------------------------
+/* This displays the informative messages describing what is going to
+ happen and then calls the download routines */
+static void RemoveDownloadNeedingItemsFromFetcher(pkgAcquire &Fetcher, bool &Transient)
+{
+ for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I < Fetcher.ItemsEnd();)
+ {
+ if ((*I)->Local == true)
+ {
+ ++I;
+ continue;
+ }
+
+ // Close the item and check if it was found in cache
+ (*I)->Finished();
+ if ((*I)->Complete == false)
+ Transient = true;
+
+ // Clear it out of the fetch list
+ delete *I;
+ I = Fetcher.ItemsBegin();
+ }
+}
+bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
+{
+ if (not RunScripts("APT::Install::Pre-Invoke"))
+ return false;
+ if (_config->FindB("APT::Get::Purge", false) == true)
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() == false; ++I)
+ if (Cache[I].Delete() == true && Cache[I].Purge() == false)
+ Cache->MarkDelete(I,true);
+
+ // Create the download object
+ auto const DownloadAllowed = _config->FindB("APT::Get::Download",true);
+ aptAcquireWithTextStatus Fetcher;
+ if (_config->FindB("APT::Get::Print-URIs", false) == true)
+ {
+ // force a hashsum for compatibility reasons
+ _config->CndSet("Acquire::ForceHash", "md5sum");
+ }
+ else if (_config->FindB("APT::Get::Simulate") == true)
+ ;
+ else if (Fetcher.GetLock(_config->FindDir("Dir::Cache::Archives")) == false)
+ return false;
+
+ // Read the source list
+ if (Cache.BuildSourceList() == false)
+ return false;
+ pkgSourceList * const List = Cache.GetSourceList();
+
+ // Create the text record parser
+ pkgRecords Recs(Cache);
+ if (_error->PendingError() == true)
+ return false;
+
+ // Create the package manager and prepare to download
+ std::unique_ptr<pkgPackageManager> PM(_system->CreatePM(Cache));
+ if (PM->GetArchives(&Fetcher,List,&Recs) == false ||
+ _error->PendingError() == true)
+ return false;
+
+ if (DownloadAllowed == false)
+ {
+ bool Missing = false;
+ RemoveDownloadNeedingItemsFromFetcher(Fetcher, Missing);
+ if (Missing)
+ {
+ if (_config->FindB("APT::Get::Fix-Missing",false))
+ PM->FixMissing();
+ else
+ return _error->Error(_("Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?"));
+ }
+ Fetcher.Shutdown();
+ if (_error->PendingError() == true)
+ return false;
+ }
+
+ // Show all the various warning indicators
+ ShowDel(c1out,Cache);
+ ShowNew(c1out,Cache);
+ if (ShwKept == true)
+ ShowKept(c1out,Cache);
+ bool const Hold = !ShowHold(c1out,Cache);
+ if (_config->FindB("APT::Get::Show-Upgraded",true) == true)
+ ShowUpgraded(c1out,Cache);
+ bool const Downgrade = !ShowDowngraded(c1out,Cache);
+
+ bool Essential = false;
+ if (_config->FindB("APT::Get::Download-Only",false) == false)
+ Essential = !ShowEssential(c1out,Cache);
+
+ Stats(c1out,Cache);
+
+ // Sanity check
+ if (Cache->BrokenCount() != 0)
+ {
+ ShowBroken(c1out,Cache,false);
+ return _error->Error(_("Internal error, InstallPackages was called with broken packages!"));
+ }
+
+ if (Cache->DelCount() == 0 && Cache->InstCount() == 0 &&
+ Cache->BadCount() == 0)
+ return RunScripts("APT::Install::Post-Invoke-Success");
+
+ // No remove flag
+ if (Cache->DelCount() != 0 && _config->FindB("APT::Get::Remove",true) == false)
+ return _error->Error(_("Packages need to be removed but remove is disabled."));
+
+ // Fail safe check
+ bool const Fail = (Essential || Downgrade || Hold);
+ if (_config->FindI("quiet",0) >= 2 ||
+ _config->FindB("APT::Get::Assume-Yes",false) == true)
+ {
+ if (Fail == true && _config->FindB("APT::Get::Force-Yes",false) == false) {
+ if (Essential == true && _config->FindB("APT::Get::allow-remove-essential", false) == false)
+ return _error->Error(_("Essential packages were removed and -y was used without --allow-remove-essential."));
+ if (Downgrade == true && _config->FindB("APT::Get::allow-downgrades", false) == false)
+ return _error->Error(_("Packages were downgraded and -y was used without --allow-downgrades."));
+ if (Hold == true && _config->FindB("APT::Get::allow-change-held-packages", false) == false)
+ return _error->Error(_("Held packages were changed and -y was used without --allow-change-held-packages."));
+ }
+ }
+
+ // Run the simulator ..
+ if (_config->FindB("APT::Get::Simulate") == true)
+ {
+ pkgSimulate PM(Cache);
+
+ APT::Progress::PackageManager *progress = APT::Progress::PackageManagerProgressFactory();
+ pkgPackageManager::OrderResult Res = PM.DoInstall(progress);
+ delete progress;
+
+ if (Res == pkgPackageManager::Failed)
+ return false;
+ if (Res != pkgPackageManager::Completed)
+ return _error->Error(_("Internal error, Ordering didn't finish"));
+ return true;
+ }
+
+ auto const FetchBytes = DownloadAllowed ? Fetcher.FetchNeeded() : 0;
+ auto const FetchPBytes = DownloadAllowed ? Fetcher.PartialPresent() : 0;
+ if (DownloadAllowed)
+ {
+ // Display statistics
+ auto const DebBytes = Fetcher.TotalNeeded();
+ if (DebBytes != Cache->DebSize())
+ {
+ c0out << "E: " << DebBytes << ',' << Cache->DebSize() << std::endl;
+ c0out << "E: " << _("How odd... The sizes didn't match, email apt@packages.debian.org") << std::endl;
+ }
+
+ // Number of bytes
+ if (DebBytes != FetchBytes)
+ //TRANSLATOR: The required space between number and unit is already included
+ // in the replacement strings, so %sB will be correctly translate in e.g. 1,5 MB
+ ioprintf(c1out,_("Need to get %sB/%sB of archives.\n"),
+ SizeToStr(FetchBytes).c_str(),SizeToStr(DebBytes).c_str());
+ else if (DebBytes != 0)
+ //TRANSLATOR: The required space between number and unit is already included
+ // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB
+ ioprintf(c1out,_("Need to get %sB of archives.\n"),
+ SizeToStr(DebBytes).c_str());
+ }
+
+ // Size delta
+ if (Cache->UsrSize() >= 0)
+ //TRANSLATOR: The required space between number and unit is already included
+ // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB
+ ioprintf(c1out,_("After this operation, %sB of additional disk space will be used.\n"),
+ SizeToStr(Cache->UsrSize()).c_str());
+ else
+ //TRANSLATOR: The required space between number and unit is already included
+ // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB
+ ioprintf(c1out,_("After this operation, %sB disk space will be freed.\n"),
+ SizeToStr(-1*Cache->UsrSize()).c_str());
+
+ if (DownloadAllowed)
+ if (CheckFreeSpaceBeforeDownload(_config->FindDir("Dir::Cache::Archives"), (FetchBytes - FetchPBytes)) == false)
+ return false;
+
+ if (_error->PendingError() == true)
+ return false;
+
+ // Just print out the uris an exit if the --print-uris flag was used
+ if (_config->FindB("APT::Get::Print-URIs") == true)
+ {
+ pkgAcquire::UriIterator I = Fetcher.UriBegin();
+ for (; I != Fetcher.UriEnd(); ++I)
+ std::cout << '\'' << I->URI << "' " << flNotDir(I->Owner->DestFile) << ' ' <<
+ std::to_string(I->Owner->FileSize) << ' ' << I->Owner->HashSum() << std::endl;
+ return true;
+ }
+
+ if (Essential == true && Safety == true && _config->FindB("APT::Get::allow-remove-essential", false) == false)
+ {
+ if (_config->FindB("APT::Get::Trivial-Only",false) == true)
+ return _error->Error(_("Trivial Only specified but this is not a trivial operation."));
+
+ // TRANSLATOR: This string needs to be typed by the user as a confirmation, so be
+ // careful with hard to type or special characters (like non-breaking spaces)
+ const char *Prompt = _("Yes, do as I say!");
+ std::string question;
+ strprintf(question,
+ _("You are about to do something potentially harmful.\n"
+ "To continue type in the phrase '%s'\n"
+ " ?] "),Prompt);
+ if (AnalPrompt(question, Prompt) == false)
+ {
+ c2out << _("Abort.") << std::endl;
+ exit(1);
+ }
+ }
+ else
+ {
+ // Prompt to continue
+ if (Ask == true || Fail == true)
+ {
+ if (_config->FindB("APT::Get::Trivial-Only",false) == true)
+ return _error->Error(_("Trivial Only specified but this is not a trivial operation."));
+
+ if (_config->FindI("quiet",0) < 2 &&
+ _config->FindB("APT::Get::Assume-Yes",false) == false)
+ {
+ if (YnPrompt(_("Do you want to continue?")) == false)
+ {
+ c2out << _("Abort.") << std::endl;
+ exit(1);
+ }
+ }
+ }
+ }
+
+ if (!CheckAuth(Fetcher, true))
+ return false;
+
+ /* Unlock the dpkg lock if we are not going to be doing an install
+ after. */
+ if (_config->FindB("APT::Get::Download-Only",false) == true)
+ _system->UnLock();
+
+ // Run it
+ bool Failed = false;
+ while (1)
+ {
+ bool Transient = false;
+ if (AcquireRun(Fetcher, 0, &Failed, &Transient) == false)
+ return false;
+
+ if (_config->FindB("APT::Get::Download-Only",false) == true)
+ {
+ if (Failed == true && _config->FindB("APT::Get::Fix-Missing",false) == false)
+ return _error->Error(_("Some files failed to download"));
+ c1out << _("Download complete and in download only mode") << std::endl;
+ return true;
+ }
+
+ if (Failed == true && _config->FindB("APT::Get::Fix-Missing",false) == false)
+ return _error->Error(_("Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?"));
+
+ if (Transient == true && Failed == true)
+ return _error->Error(_("--fix-missing and media swapping is not currently supported"));
+
+ // Try to deal with missing package files
+ if (Failed == true && PM->FixMissing() == false)
+ {
+ c2out << _("Unable to correct missing packages.") << std::endl;
+ return _error->Error(_("Aborting install."));
+ }
+
+ auto const progress = APT::Progress::PackageManagerProgressFactory();
+ _system->UnLockInner();
+ pkgPackageManager::OrderResult const Res = PM->DoInstall(progress);
+ delete progress;
+
+ if (Res == pkgPackageManager::Failed || _error->PendingError() == true)
+ return false;
+ if (Res == pkgPackageManager::Completed)
+ break;
+
+ _system->LockInner();
+
+ // Reload the fetcher object and loop again for media swapping
+ Fetcher.Shutdown();
+ if (PM->GetArchives(&Fetcher,List,&Recs) == false)
+ return false;
+
+ Failed = false;
+ if (DownloadAllowed == false)
+ RemoveDownloadNeedingItemsFromFetcher(Fetcher, Failed);
+ }
+
+ std::set<std::string> const disappearedPkgs = PM->GetDisappearedPackages();
+ if (disappearedPkgs.empty() == false)
+ {
+ ShowList(c1out, P_("The following package disappeared from your system as\n"
+ "all files have been overwritten by other packages:",
+ "The following packages disappeared from your system as\n"
+ "all files have been overwritten by other packages:", disappearedPkgs.size()), disappearedPkgs,
+ [](std::string const &Pkg) { return Pkg.empty() == false; },
+ [](std::string const &Pkg) { return Pkg; },
+ [](std::string const &) { return std::string(); });
+ c0out << _("Note: This is done automatically and on purpose by dpkg.") << std::endl;
+ }
+
+ // cleanup downloaded debs
+ if (_config->FindB("APT::Keep-Downloaded-Packages", true) == false)
+ {
+ std::string const archivedir = _config->FindDir("Dir::Cache::archives");
+ for (auto I = Fetcher.ItemsBegin(); I != Fetcher.ItemsEnd(); ++I)
+ {
+ if (flNotFile((*I)->DestFile) != archivedir || (*I)->Local)
+ continue;
+ RemoveFile("Keep-Downloaded-Packages=false", (*I)->DestFile);
+ }
+ }
+
+ if (not RunScripts("APT::Install::Post-Invoke-Success"))
+ return false;
+
+ return true;
+}
+ /*}}}*/
+// DoAutomaticRemove - Remove all automatic unused packages /*{{{*/
+// ---------------------------------------------------------------------
+/* Remove unused automatic packages */
+bool DoAutomaticRemove(CacheFile &Cache)
+{
+ bool Debug = _config->FindB("Debug::pkgAutoRemove",false);
+ bool doAutoRemove = _config->FindB("APT::Get::AutomaticRemove", false);
+ bool doAutoRemoveKernels = _config->FindB("APT::Get::AutomaticRemove::Kernels", false);
+ bool hideAutoRemove = _config->FindB("APT::Get::HideAutoRemove");
+
+ std::unique_ptr<APT::CacheFilter::Matcher> kernelAutoremovalMatcher;
+ if (doAutoRemoveKernels && !doAutoRemove)
+ {
+ kernelAutoremovalMatcher = APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(Cache, true);
+ }
+
+ pkgDepCache::ActionGroup group(*Cache);
+ if(Debug)
+ std::cout << "DoAutomaticRemove()" << std::endl;
+
+ if (doAutoRemove == true &&
+ _config->FindB("APT::Get::Remove",true) == false)
+ {
+ c1out << _("We are not supposed to delete stuff, can't start "
+ "AutoRemover") << std::endl;
+ return false;
+ }
+
+ bool purgePkgs = _config->FindB("APT::Get::Purge", false);
+ bool smallList = (hideAutoRemove == false &&
+ strcasecmp(_config->Find("APT::Get::HideAutoRemove","").c_str(),"small") == 0);
+
+ unsigned long autoRemoveCount = 0;
+ APT::PackageSet tooMuch;
+ SortedPackageUniverse Universe(Cache);
+ // look over the cache to see what can be removed
+ for (auto const &Pkg: Universe)
+ {
+ if (Cache[Pkg].Garbage)
+ {
+ if(Pkg.CurrentVer() != 0 || Cache[Pkg].Install())
+ if(Debug)
+ std::cout << "We could delete " << APT::PrettyPkg(Cache, Pkg) << std::endl;
+
+ if (doAutoRemove || (kernelAutoremovalMatcher != nullptr && (*kernelAutoremovalMatcher)(Pkg)))
+ {
+ if(Pkg.CurrentVer() != 0 &&
+ Pkg->CurrentState != pkgCache::State::ConfigFiles)
+ Cache->MarkDelete(Pkg, purgePkgs, 0, false);
+ else
+ Cache->MarkKeep(Pkg, false, false);
+ }
+ else
+ {
+ // if the package is a new install and already garbage we don't need to
+ // install it in the first place, so nuke it instead of show it
+ if (Cache[Pkg].Install() == true && Pkg.CurrentVer() == 0)
+ {
+ tooMuch.insert(Pkg);
+ Cache->MarkDelete(Pkg, false, 0, false);
+ }
+ // only show stuff in the list that is not yet marked for removal
+ else if(hideAutoRemove == false && Cache[Pkg].Delete() == false)
+ ++autoRemoveCount;
+ }
+ }
+ }
+
+ // we could have removed a new dependency of a garbage package,
+ // so check if a reverse depends is broken and if so install it again.
+ if (tooMuch.empty() == false && (Cache->BrokenCount() != 0 || Cache->PolicyBrokenCount() != 0))
+ {
+ bool Changed;
+ do {
+ Changed = false;
+ for (APT::PackageSet::iterator Pkg = tooMuch.begin();
+ Pkg != tooMuch.end(); ++Pkg)
+ {
+ APT::PackageSet too;
+ too.insert(*Pkg);
+ for (pkgCache::PrvIterator Prv = Cache[Pkg].CandidateVerIter(Cache).ProvidesList();
+ Prv.end() == false; ++Prv)
+ too.insert(Prv.ParentPkg());
+ for (APT::PackageSet::const_iterator P = too.begin(); P != too.end(); ++P)
+ {
+ for (pkgCache::DepIterator R = P.RevDependsList();
+ R.end() == false; ++R)
+ {
+ if (R.IsNegative() == true ||
+ Cache->IsImportantDep(R) == false)
+ continue;
+ auto const RV = R.ParentVer();
+ if (unlikely(RV.end() == true))
+ continue;
+ auto const RP = RV.ParentPkg();
+ // check if that dependency comes from an interesting version
+ if (RP.CurrentVer() == RV)
+ {
+ if ((*Cache)[RP].Keep() == false)
+ continue;
+ }
+ else if (Cache[RP].CandidateVerIter(Cache) == RV)
+ {
+ if ((*Cache)[RP].NewInstall() == false && (*Cache)[RP].Upgrade() == false)
+ continue;
+ }
+ else // ignore dependency from a non-candidate version
+ continue;
+ if (Debug == true)
+ std::clog << "Save " << APT::PrettyPkg(Cache, Pkg) << " as another installed package depends on it: " << APT::PrettyPkg(Cache, RP) << std::endl;
+ Cache->MarkInstall(Pkg, false, 0, false);
+ if (hideAutoRemove == false)
+ ++autoRemoveCount;
+ tooMuch.erase(Pkg);
+ Changed = true;
+ break;
+ }
+ if (Changed == true)
+ break;
+ }
+ if (Changed == true)
+ break;
+ }
+ } while (Changed == true);
+ }
+ // trigger marking now so that the package list below is correct
+ group.release();
+
+ // Now see if we had destroyed anything (if we had done anything)
+ if (Cache->BrokenCount() != 0)
+ {
+ c1out << _("Hmm, seems like the AutoRemover destroyed something which really\n"
+ "shouldn't happen. Please file a bug report against apt.") << std::endl;
+ c1out << std::endl;
+ c1out << _("The following information may help to resolve the situation:") << std::endl;
+ c1out << std::endl;
+ ShowBroken(c1out,Cache,false);
+
+ return _error->Error(_("Internal Error, AutoRemover broke stuff"));
+ }
+
+ // if we don't remove them, we should show them!
+ if (doAutoRemove == false && autoRemoveCount != 0)
+ {
+ if (smallList == false)
+ {
+ SortedPackageUniverse Universe(Cache);
+ ShowList(c1out, P_("The following package was automatically installed and is no longer required:",
+ "The following packages were automatically installed and are no longer required:",
+ autoRemoveCount), Universe,
+ [&Cache](pkgCache::PkgIterator const &Pkg) { return (*Cache)[Pkg].Garbage == true && (*Cache)[Pkg].Delete() == false; },
+ &PrettyFullName, CandidateVersion(&Cache));
+ }
+ else
+ ioprintf(c1out, P_("%lu package was automatically installed and is no longer required.\n",
+ "%lu packages were automatically installed and are no longer required.\n", autoRemoveCount), autoRemoveCount);
+ std::string autocmd = "apt autoremove";
+ if (getenv("SUDO_USER") != nullptr)
+ {
+ auto const envsudocmd = getenv("SUDO_COMMAND");
+ auto const envshell = getenv("SHELL");
+ if (envsudocmd == nullptr || envshell == nullptr || strcmp(envsudocmd, envshell) != 0)
+ autocmd = "sudo " + autocmd;
+ }
+ ioprintf(c1out, P_("Use '%s' to remove it.", "Use '%s' to remove them.", autoRemoveCount), autocmd.c_str());
+ c1out << std::endl;
+ }
+ return true;
+}
+ /*}}}*/
+// DoCacheManipulationFromCommandLine /*{{{*/
+static const unsigned short MOD_REMOVE = 1;
+static const unsigned short MOD_INSTALL = 2;
+
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache, int UpgradeMode)
+{
+ std::vector<PseudoPkg> VolatileCmdL;
+ return DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, UpgradeMode);
+}
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, std::vector<PseudoPkg> &VolatileCmdL, CacheFile &Cache, int UpgradeMode)
+{
+ std::map<unsigned short, APT::VersionSet> verset;
+ std::set<std::string> UnknownPackages;
+ return DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, verset, UpgradeMode, UnknownPackages);
+}
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, std::vector<PseudoPkg> &VolatileCmdL, CacheFile &Cache,
+ std::map<unsigned short, APT::VersionSet> &verset, int UpgradeMode, std::set<std::string> &UnknownPackages)
+{
+ // Enter the special broken fixing mode if the user specified arguments
+ bool BrokenFix = false;
+ if (Cache->BrokenCount() != 0)
+ BrokenFix = true;
+
+ std::unique_ptr<pkgProblemResolver> Fix(nullptr);
+ if (_config->FindB("APT::Get::CallResolver", true) == true)
+ Fix.reset(new pkgProblemResolver(Cache));
+
+ unsigned short fallback = MOD_INSTALL;
+ if (strcasecmp(CmdL.FileList[0], "reinstall") == 0)
+ _config->Set("APT::Get::ReInstall", "true");
+ else if (strcasecmp(CmdL.FileList[0],"remove") == 0)
+ fallback = MOD_REMOVE;
+ else if (strcasecmp(CmdL.FileList[0], "purge") == 0)
+ {
+ _config->Set("APT::Get::Purge", true);
+ fallback = MOD_REMOVE;
+ }
+ else if (strcasecmp(CmdL.FileList[0], "autoremove") == 0 ||
+ strcasecmp(CmdL.FileList[0], "auto-remove") == 0)
+ {
+ _config->Set("APT::Get::AutomaticRemove", "true");
+ fallback = MOD_REMOVE;
+ }
+ else if (strcasecmp(CmdL.FileList[0], "autopurge") == 0)
+ {
+ _config->Set("APT::Get::AutomaticRemove", "true");
+ _config->Set("APT::Get::Purge", true);
+ fallback = MOD_REMOVE;
+ }
+
+ std::list<APT::VersionSet::Modifier> mods;
+ mods.push_back(APT::VersionSet::Modifier(MOD_INSTALL, "+",
+ APT::VersionSet::Modifier::POSTFIX, APT::CacheSetHelper::CANDIDATE));
+ mods.push_back(APT::VersionSet::Modifier(MOD_REMOVE, "-",
+ APT::VersionSet::Modifier::POSTFIX, APT::CacheSetHelper::NEWEST));
+ CacheSetHelperAPTGet helper(c0out);
+ verset = APT::VersionSet::GroupedFromCommandLine(Cache,
+ CmdL.FileList + 1, mods, fallback, helper);
+
+ for (auto const &I: VolatileCmdL)
+ {
+ pkgCache::PkgIterator const P = Cache->FindPkg(I.name);
+ if (P.end())
+ continue;
+
+ // Set any version providing the .deb as the candidate.
+ for (auto Prv = P.ProvidesList(); Prv.end() == false; Prv++)
+ {
+ if (I.release.empty())
+ Cache.GetDepCache()->SetCandidateVersion(Prv.OwnerVer());
+ else
+ Cache.GetDepCache()->SetCandidateRelease(Prv.OwnerVer(), I.release);
+ }
+
+ // via cacheset to have our usual virtual handling
+ APT::VersionContainerInterface::FromPackage(&(verset[MOD_INSTALL]), Cache, P, APT::CacheSetHelper::CANDIDATE, helper);
+ }
+
+ UnknownPackages = helper.notFound;
+
+ if (_error->PendingError() == true)
+ {
+ helper.showVirtualPackageErrors(Cache);
+ return false;
+ }
+
+
+ TryToInstall InstallAction(Cache, Fix.get(), BrokenFix);
+ TryToRemove RemoveAction(Cache, Fix.get());
+
+ // new scope for the ActionGroup
+ {
+ pkgDepCache::ActionGroup group(Cache);
+ unsigned short const order[] = { MOD_REMOVE, MOD_INSTALL, 0 };
+
+ for (unsigned short i = 0; order[i] != 0; ++i)
+ {
+ if (order[i] == MOD_INSTALL)
+ InstallAction = std::for_each(verset[MOD_INSTALL].begin(), verset[MOD_INSTALL].end(), InstallAction);
+ else if (order[i] == MOD_REMOVE)
+ RemoveAction = std::for_each(verset[MOD_REMOVE].begin(), verset[MOD_REMOVE].end(), RemoveAction);
+ }
+
+ if (Fix != NULL && _config->FindB("APT::Get::AutoSolving", true) == true)
+ {
+ InstallAction.propergateReleaseCandiateSwitching(helper.selectedByRelease, c0out);
+ InstallAction.doAutoInstall();
+ }
+
+ if (_error->PendingError() == true)
+ {
+ return false;
+ }
+
+ /* If we are in the Broken fixing mode we do not attempt to fix the
+ problems. This is if the user invoked install without -f and gave
+ packages */
+ if (BrokenFix == true && Cache->BrokenCount() != 0)
+ {
+ c1out << _("You might want to run 'apt --fix-broken install' to correct these.") << std::endl;
+ ShowBroken(c1out,Cache,false);
+ return _error->Error(_("Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution)."));
+ }
+
+ if (Fix != NULL)
+ {
+ // Call the scored problem resolver
+ OpTextProgress Progress(*_config);
+ bool const distUpgradeMode = strcmp(CmdL.FileList[0], "dist-upgrade") == 0 || strcmp(CmdL.FileList[0], "full-upgrade") == 0;
+
+ if (distUpgradeMode && _config->Find("Binary") == "apt")
+ _config->CndSet("APT::Get::AutomaticRemove::Kernels", _config->FindB("APT::Get::AutomaticRemove", true));
+
+ bool resolver_fail = false;
+ if (distUpgradeMode == true || UpgradeMode != APT::Upgrade::ALLOW_EVERYTHING)
+ resolver_fail = APT::Upgrade::Upgrade(Cache, UpgradeMode, &Progress);
+ else
+ resolver_fail = Fix->Resolve(true, &Progress);
+
+ if (resolver_fail == false && Cache->BrokenCount() == 0)
+ return false;
+ }
+
+ if (CheckNothingBroken(Cache) == false)
+ return false;
+ }
+ if (!DoAutomaticRemove(Cache))
+ return false;
+
+ // if nothing changed in the cache, but only the automark information
+ // we write the StateFile here, otherwise it will be written in
+ // cache.commit()
+ if (InstallAction.AutoMarkChanged > 0 &&
+ Cache->DelCount() == 0 && Cache->InstCount() == 0 &&
+ Cache->BadCount() == 0 &&
+ _config->FindB("APT::Get::Simulate",false) == false)
+ Cache->writeStateFile(NULL);
+
+ return true;
+}
+ /*}}}*/
+bool AddVolatileSourceFile(pkgSourceList *const SL, PseudoPkg &&pkg, std::vector<PseudoPkg> &VolatileCmdL)/*{{{*/
+{
+ auto const ext = flExtension(pkg.name);
+ if (ext != "dsc" && FileExists(pkg.name + "/debian/control") == false)
+ return false;
+ std::vector<std::string> files;
+ SL->AddVolatileFile(pkg.name, &files);
+ std::transform(files.begin(), files.end(), std::back_inserter(VolatileCmdL), [&](auto &&f) { return PseudoPkg{std::move(f), pkg.arch, pkg.release, pkg.index}; });
+ return true;
+
+}
+ /*}}}*/
+bool AddVolatileBinaryFile(pkgSourceList *const SL, PseudoPkg &&pkg, std::vector<PseudoPkg> &VolatileCmdL)/*{{{*/
+{
+ auto const ext = flExtension(pkg.name);
+ if (ext != "deb" && ext != "ddeb" && ext != "changes")
+ return false;
+ std::vector<std::string> files;
+ SL->AddVolatileFile(pkg.name, &files);
+ std::transform(files.begin(), files.end(), std::back_inserter(VolatileCmdL), [&](auto &&f) { return PseudoPkg{std::move(f), pkg.arch, pkg.release, pkg.index}; });
+ return true;
+}
+ /*}}}*/
+static bool AddIfVolatile(pkgSourceList *const SL, std::vector<PseudoPkg> &VolatileCmdL, bool (*Add)(pkgSourceList *const, PseudoPkg &&, std::vector<PseudoPkg> &), char const * const I, std::string const &pseudoArch)/*{{{*/
+{
+ if (I != nullptr && (I[0] == '/' || (I[0] == '.' && (I[1] == '\0' || (I[1] == '.' && (I[2] == '\0' || I[2] == '/')) || I[1] == '/'))))
+ {
+ PseudoPkg pkg(I, pseudoArch, "", SL->GetVolatileFiles().size());
+ if (FileExists(I)) // this accepts directories and symlinks, too
+ {
+ if (Add(SL, std::move(pkg), VolatileCmdL))
+ ;
+ else
+ _error->Error(_("Unsupported file %s given on commandline"), I);
+ return true;
+ }
+ else
+ {
+ auto const found = pkg.name.rfind("/");
+ if (found == pkg.name.find("/"))
+ _error->Error(_("Unsupported file %s given on commandline"), I);
+ else
+ {
+ pkg.release = pkg.name.substr(found + 1);
+ pkg.name.erase(found);
+ if (Add(SL, std::move(pkg), VolatileCmdL))
+ ;
+ else
+ _error->Error(_("Unsupported file %s given on commandline"), I);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+ /*}}}*/
+std::vector<PseudoPkg> GetAllPackagesAsPseudo(pkgSourceList *const SL, CommandLine &CmdL, bool (*Add)(pkgSourceList *const, PseudoPkg &&, std::vector<PseudoPkg> &), std::string const &pseudoArch)/*{{{*/
+{
+ std::vector<PseudoPkg> PkgCmdL;
+ std::for_each(CmdL.FileList + 1, CmdL.FileList + CmdL.FileSize(), [&](char const *const I) {
+ if (AddIfVolatile(SL, PkgCmdL, Add, I, pseudoArch) == false)
+ PkgCmdL.emplace_back(I, pseudoArch, "", -1);
+ });
+ return PkgCmdL;
+}
+ /*}}}*/
+std::vector<PseudoPkg> GetPseudoPackages(pkgSourceList *const SL, CommandLine &CmdL, bool (*Add)(pkgSourceList *const, PseudoPkg &&, std::vector<PseudoPkg> &), std::string const &pseudoArch)/*{{{*/
+{
+ std::vector<PseudoPkg> VolatileCmdL;
+ std::remove_if(CmdL.FileList + 1, CmdL.FileList + 1 + CmdL.FileSize(), [&](char const *const I) {
+ return AddIfVolatile(SL, VolatileCmdL, Add, I, pseudoArch);
+ });
+ return VolatileCmdL;
+}
+ /*}}}*/
+// DoInstall - Install packages from the command line /*{{{*/
+// ---------------------------------------------------------------------
+/* Install named packages */
+struct PkgIsExtraInstalled {
+ pkgCacheFile * const Cache;
+ APT::VersionSet const * const verset;
+ PkgIsExtraInstalled(pkgCacheFile * const Cache, APT::VersionSet const * const Container) : Cache(Cache), verset(Container) {}
+ bool operator() (pkgCache::PkgIterator const &Pkg)
+ {
+ if ((*Cache)[Pkg].Install() == false)
+ return false;
+ pkgCache::VerIterator const Cand = (*Cache)[Pkg].CandidateVerIter(*Cache);
+ return verset->find(Cand) == verset->end();
+ }
+};
+bool DoInstall(CommandLine &CmdL)
+{
+ CacheFile Cache;
+ auto VolatileCmdL = GetPseudoPackages(Cache.GetSourceList(), CmdL, AddVolatileBinaryFile, "");
+
+ // then open the cache
+ if (Cache.OpenForInstall() == false ||
+ Cache.CheckDeps(CmdL.FileSize() != 1) == false)
+ return false;
+
+ std::map<unsigned short, APT::VersionSet> verset;
+ std::set<std::string> UnknownPackages;
+
+ if (!DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, verset, 0, UnknownPackages))
+ {
+ RunJsonHook("AptCli::Hooks::Install", "org.debian.apt.hooks.install.fail", CmdL.FileList, Cache, UnknownPackages);
+ return false;
+ }
+
+ /* Print out a list of packages that are going to be installed extra
+ to what the user asked */
+ SortedPackageUniverse Universe(Cache);
+ if (Cache->InstCount() != verset[MOD_INSTALL].size())
+ ShowList(c1out, _("The following additional packages will be installed:"), Universe,
+ PkgIsExtraInstalled(&Cache, &verset[MOD_INSTALL]),
+ &PrettyFullName, CandidateVersion(&Cache));
+
+ /* Print out a list of suggested and recommended packages */
+ {
+ std::list<std::string> Recommends, Suggests, SingleRecommends, SingleSuggests;
+ for (auto const &Pkg: Universe)
+ {
+ /* Just look at the ones we want to install */
+ if ((*Cache)[Pkg].Install() == false)
+ continue;
+
+ // get the recommends/suggests for the candidate ver
+ pkgCache::VerIterator CV = (*Cache)[Pkg].CandidateVerIter(*Cache);
+ for (pkgCache::DepIterator D = CV.DependsList(); D.end() == false; )
+ {
+ pkgCache::DepIterator Start;
+ pkgCache::DepIterator End;
+ D.GlobOr(Start,End); // advances D
+ if (Start->Type != pkgCache::Dep::Recommends && Start->Type != pkgCache::Dep::Suggests)
+ continue;
+
+ {
+ // Skip if we already saw this
+ std::string target;
+ for (pkgCache::DepIterator I = Start; I != D; ++I)
+ {
+ if (target.empty() == false)
+ target.append(" | ");
+ target.append(I.TargetPkg().FullName(true));
+ }
+ std::list<std::string> &Type = Start->Type == pkgCache::Dep::Recommends ? SingleRecommends : SingleSuggests;
+ if (std::find(Type.begin(), Type.end(), target) != Type.end())
+ continue;
+ Type.push_back(target);
+ }
+
+ std::list<std::string> OrList;
+ bool foundInstalledInOrGroup = false;
+ for (pkgCache::DepIterator I = Start; I != D; ++I)
+ {
+ {
+ // satisfying package is installed and not marked for deletion
+ APT::VersionList installed = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::INSTALLED);
+ if (std::find_if(installed.begin(), installed.end(),
+ [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Delete() == false; }) != installed.end())
+ {
+ foundInstalledInOrGroup = true;
+ break;
+ }
+ }
+
+ {
+ // satisfying package is upgraded to/new install
+ APT::VersionList upgrades = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::CANDIDATE);
+ if (std::find_if(upgrades.begin(), upgrades.end(),
+ [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Upgrade(); }) != upgrades.end())
+ {
+ foundInstalledInOrGroup = true;
+ break;
+ }
+ }
+
+ if (OrList.empty())
+ OrList.push_back(I.TargetPkg().FullName(true));
+ else
+ OrList.push_back("| " + I.TargetPkg().FullName(true));
+ }
+
+ if(foundInstalledInOrGroup == false)
+ {
+ std::list<std::string> &Type = Start->Type == pkgCache::Dep::Recommends ? Recommends : Suggests;
+ std::move(OrList.begin(), OrList.end(), std::back_inserter(Type));
+ }
+ }
+ }
+ auto always_true = [](std::string const&) { return true; };
+ auto string_ident = [](std::string const&str) { return str; };
+ auto verbose_show_candidate =
+ [&Cache](std::string str)
+ {
+ if (APT::String::Startswith(str, "| "))
+ str.erase(0, 2);
+ pkgCache::PkgIterator const Pkg = Cache->FindPkg(str);
+ if (Pkg.end() == true)
+ return "";
+ return (*Cache)[Pkg].CandVersion;
+ };
+ ShowList(c1out,_("Suggested packages:"), Suggests,
+ always_true, string_ident, verbose_show_candidate);
+ ShowList(c1out,_("Recommended packages:"), Recommends,
+ always_true, string_ident, verbose_show_candidate);
+ }
+
+ RunJsonHook("AptCli::Hooks::Install", "org.debian.apt.hooks.install.pre-prompt", CmdL.FileList, Cache);
+
+ bool result;
+ // See if we need to prompt
+ // FIXME: check if really the packages in the set are going to be installed
+ if (Cache->InstCount() == verset[MOD_INSTALL].size() && Cache->DelCount() == 0)
+ result = InstallPackages(Cache, false, false);
+ else
+ result = InstallPackages(Cache, false);
+
+ if (result)
+ result = RunJsonHook("AptCli::Hooks::Install", "org.debian.apt.hooks.install.post", CmdL.FileList, Cache);
+ else
+ /* not a result */ RunJsonHook("AptCli::Hooks::Install", "org.debian.apt.hooks.install.fail", CmdL.FileList, Cache);
+
+ return result;
+}
+ /*}}}*/
+
+// TryToInstall - Mark a package for installation /*{{{*/
+void TryToInstall::operator() (pkgCache::VerIterator const &Ver) {
+ if (unlikely(Ver.end()))
+ {
+ _error->Fatal("The given version to TryToInstall is invalid!");
+ return;
+ }
+ pkgCache::PkgIterator Pkg = Ver.ParentPkg();
+ if (unlikely(Pkg.end()))
+ {
+ _error->Fatal("The given version to TryToInstall has an invalid parent package!");
+ return;
+ }
+
+ Cache->GetDepCache()->SetCandidateVersion(Ver);
+ pkgDepCache::StateCache &State = (*Cache)[Pkg];
+
+ // Handle the no-upgrade case
+ if (_config->FindB("APT::Get::upgrade",true) == false && Pkg->CurrentVer != 0)
+ ioprintf(c1out,_("Skipping %s, it is already installed and upgrade is not set.\n"),
+ Pkg.FullName(true).c_str());
+ // Ignore request for install if package would be new
+ else if (_config->FindB("APT::Get::Only-Upgrade", false) == true && Pkg->CurrentVer == 0)
+ ioprintf(c1out,_("Skipping %s, it is not installed and only upgrades are requested.\n"),
+ Pkg.FullName(true).c_str());
+ else {
+ if (Fix != NULL) {
+ Fix->Clear(Pkg);
+ Fix->Protect(Pkg);
+ }
+ Cache->GetDepCache()->MarkInstall(Pkg,false);
+
+ if (State.Install() == false) {
+ if (_config->FindB("APT::Get::ReInstall",false) == true) {
+ if (Pkg->CurrentVer == 0 || Pkg.CurrentVer().Downloadable() == false)
+ ioprintf(c1out,_("Reinstallation of %s is not possible, it cannot be downloaded.\n"),
+ Pkg.FullName(true).c_str());
+ else
+ Cache->GetDepCache()->SetReInstall(Pkg, true);
+ } else
+ // TRANSLATORS: First string is package name, second is version
+ ioprintf(c1out,_("%s is already the newest version (%s).\n"),
+ Pkg.FullName(true).c_str(), Pkg.CurrentVer().VerStr());
+ }
+
+ // Install it with autoinstalling enabled (if we not respect the minial
+ // required deps or the policy)
+ if (FixBroken == false)
+ doAutoInstallLater.insert(Pkg);
+ }
+
+ // see if we need to fix the auto-mark flag
+ // e.g. apt-get install foo
+ // where foo is marked automatic
+ if (State.Install() == false &&
+ (State.Flags & pkgCache::Flag::Auto) &&
+ _config->FindB("APT::Get::ReInstall",false) == false &&
+ _config->FindB("APT::Get::Only-Upgrade",false) == false &&
+ _config->FindB("APT::Get::Download-Only",false) == false)
+ {
+ ioprintf(c1out,_("%s set to manually installed.\n"),
+ Pkg.FullName(true).c_str());
+ Cache->GetDepCache()->MarkAuto(Pkg,false);
+ AutoMarkChanged++;
+ }
+}
+ /*}}}*/
+bool TryToInstall::propergateReleaseCandiateSwitching(std::list<std::pair<pkgCache::VerIterator, std::string> > const &start, std::ostream &out)/*{{{*/
+{
+ for (std::list<std::pair<pkgCache::VerIterator, std::string> >::const_iterator s = start.begin();
+ s != start.end(); ++s)
+ Cache->GetDepCache()->SetCandidateVersion(s->first);
+
+ bool Success = true;
+ // the Changed list contains:
+ // first: "new version"
+ // second: "what-caused the change"
+ std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > Changed;
+ for (std::list<std::pair<pkgCache::VerIterator, std::string> >::const_iterator s = start.begin();
+ s != start.end(); ++s)
+ {
+ Changed.push_back(std::make_pair(s->first, pkgCache::VerIterator(*Cache)));
+ // We continue here even if it failed to enhance the ShowBroken output
+ Success &= Cache->GetDepCache()->SetCandidateRelease(s->first, s->second, Changed);
+ }
+ for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = Changed.begin();
+ c != Changed.end(); ++c)
+ {
+ if (c->second.end() == true)
+ {
+ auto const pkgname = c->first.ParentPkg().FullName(true);
+ if (APT::String::Startswith(pkgname, "builddeps:"))
+ continue;
+ ioprintf(out, _("Selected version '%s' (%s) for '%s'\n"),
+ c->first.VerStr(), c->first.RelStr().c_str(), pkgname.c_str());
+ }
+ else if (c->first.ParentPkg()->Group != c->second.ParentPkg()->Group)
+ {
+ auto pkgname = c->second.ParentPkg().FullName(true);
+ if (APT::String::Startswith(pkgname, "builddeps:"))
+ pkgname.replace(0, strlen("builddeps"), "src");
+ pkgCache::VerIterator V = (*Cache)[c->first.ParentPkg()].CandidateVerIter(*Cache);
+ ioprintf(out, _("Selected version '%s' (%s) for '%s' because of '%s'\n"), V.VerStr(),
+ V.RelStr().c_str(), V.ParentPkg().FullName(true).c_str(), pkgname.c_str());
+ }
+ }
+ return Success;
+}
+ /*}}}*/
+void TryToInstall::doAutoInstall() { /*{{{*/
+ for (APT::PackageSet::const_iterator P = doAutoInstallLater.begin();
+ P != doAutoInstallLater.end(); ++P) {
+ pkgDepCache::StateCache &State = (*Cache)[P];
+ if (State.InstBroken() == false && State.InstPolicyBroken() == false)
+ continue;
+ Cache->GetDepCache()->MarkInstall(P, true);
+ }
+ doAutoInstallLater.clear();
+}
+ /*}}}*/
+// TryToRemove - Mark a package for removal /*{{{*/
+void TryToRemove::operator() (pkgCache::VerIterator const &Ver)
+{
+ pkgCache::PkgIterator Pkg = Ver.ParentPkg();
+
+ if (Fix != NULL)
+ {
+ Fix->Clear(Pkg);
+ Fix->Protect(Pkg);
+ Fix->Remove(Pkg);
+ }
+
+ if ((Pkg->CurrentVer == 0 && PurgePkgs == false) ||
+ (PurgePkgs == true && Pkg->CurrentState == pkgCache::State::NotInstalled))
+ {
+ pkgCache::GrpIterator Grp = Pkg.Group();
+ pkgCache::PkgIterator P = Grp.PackageList();
+ for (; P.end() != true; P = Grp.NextPkg(P))
+ {
+ if (P == Pkg)
+ continue;
+ if (P->CurrentVer != 0 || (PurgePkgs == true && P->CurrentState != pkgCache::State::NotInstalled))
+ {
+ // TRANSLATORS: Note, this is not an interactive question
+ ioprintf(c1out,_("Package '%s' is not installed, so not removed. Did you mean '%s'?\n"),
+ Pkg.FullName(true).c_str(), P.FullName(true).c_str());
+ break;
+ }
+ }
+ if (P.end() == true)
+ ioprintf(c1out,_("Package '%s' is not installed, so not removed\n"),Pkg.FullName(true).c_str());
+
+ // MarkInstall refuses to install packages on hold
+ Pkg->SelectedState = pkgCache::State::Hold;
+ }
+ else
+ Cache->GetDepCache()->MarkDelete(Pkg, PurgePkgs);
+}
+ /*}}}*/
diff --git a/apt-private/private-install.h b/apt-private/private-install.h
new file mode 100644
index 0000000..39a040e
--- /dev/null
+++ b/apt-private/private-install.h
@@ -0,0 +1,75 @@
+#ifndef APT_PRIVATE_INSTALL_H
+#define APT_PRIVATE_INSTALL_H
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+
+#include <list>
+#include <string>
+#include <utility>
+
+class CacheFile;
+class CommandLine;
+class pkgProblemResolver;
+
+APT_PUBLIC bool DoInstall(CommandLine &Cmd);
+
+struct PseudoPkg
+{
+ std::string name;
+ std::string arch;
+ std::string release;
+ ssize_t index;
+ PseudoPkg(std::string const &n, std::string const &a, std::string const &r) : name(n), arch(a), release(r), index(-1) {}
+ PseudoPkg(std::string const &n, std::string const &a, std::string const &r, ssize_t i) : name(n), arch(a), release(r), index(i) {}
+};
+std::vector<PseudoPkg> GetAllPackagesAsPseudo(pkgSourceList *const SL, CommandLine &CmdL, bool (*Add)(pkgSourceList *const, PseudoPkg &&, std::vector<PseudoPkg> &), std::string const &pseudoArch);
+std::vector<PseudoPkg> GetPseudoPackages(pkgSourceList *const SL, CommandLine &CmdL, bool (*Add)(pkgSourceList *const, PseudoPkg &&, std::vector<PseudoPkg> &), std::string const &pseudoArch);
+bool AddVolatileBinaryFile(pkgSourceList *const SL, PseudoPkg &&pkg, std::vector<PseudoPkg> &VolatileCmdL);
+bool AddVolatileSourceFile(pkgSourceList *const SL, PseudoPkg &&pkg, std::vector<PseudoPkg> &VolatileCmdL);
+
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, std::vector<PseudoPkg> &VolatileCmdL, CacheFile &Cache,
+ std::map<unsigned short, APT::VersionSet> &verset, int UpgradeMode, std::set<std::string> &UnknownPackages);
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, std::vector<PseudoPkg> &VolatileCmdL, CacheFile &Cache, int UpgradeMode);
+bool DoCacheManipulationFromCommandLine(CommandLine &CmdL, CacheFile &Cache, int UpgradeMode);
+
+APT_PUBLIC bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask = true,
+ bool Safety = true);
+
+bool CheckNothingBroken(CacheFile &Cache);
+bool DoAutomaticRemove(CacheFile &Cache);
+
+// TryToInstall - Mark a package for installation /*{{{*/
+struct TryToInstall {
+ pkgCacheFile* Cache;
+ pkgProblemResolver* Fix;
+ bool FixBroken;
+ unsigned long AutoMarkChanged;
+ APT::PackageSet doAutoInstallLater;
+
+ TryToInstall(pkgCacheFile &Cache, pkgProblemResolver *PM, bool const FixBroken) : Cache(&Cache), Fix(PM),
+ FixBroken(FixBroken), AutoMarkChanged(0) {};
+
+ void operator() (pkgCache::VerIterator const &Ver);
+ bool propergateReleaseCandiateSwitching(std::list<std::pair<pkgCache::VerIterator, std::string> > const &start, std::ostream &out);
+ void doAutoInstall();
+};
+ /*}}}*/
+// TryToRemove - Mark a package for removal /*{{{*/
+struct TryToRemove {
+ pkgCacheFile* Cache;
+ pkgProblemResolver* Fix;
+ bool PurgePkgs;
+
+ TryToRemove(pkgCacheFile &Cache, pkgProblemResolver *PM) : Cache(&Cache), Fix(PM),
+ PurgePkgs(_config->FindB("APT::Get::Purge", false)) {};
+
+ void operator() (pkgCache::VerIterator const &Ver);
+};
+ /*}}}*/
+
+
+#endif
diff --git a/apt-private/private-json-hooks.cc b/apt-private/private-json-hooks.cc
new file mode 100644
index 0000000..6bf70b1
--- /dev/null
+++ b/apt-private/private-json-hooks.cc
@@ -0,0 +1,461 @@
+/*
+ * private-json-hooks.cc - 2nd generation, JSON-RPC, hooks for APT
+ *
+ * Copyright (c) 2018 Canonical Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <apt-pkg/debsystem.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/strutl.h>
+#include <apt-private/private-json-hooks.h>
+#include <apt-private/private-output.h>
+
+#include <iomanip>
+#include <ostream>
+#include <sstream>
+#include <stack>
+
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+/**
+ * @brief Simple JSON writer
+ *
+ * This performs no error checking, so be careful.
+ */
+class APT_HIDDEN JsonWriter
+{
+ std::ostream &os;
+ std::locale old_locale;
+
+ enum write_state
+ {
+ empty,
+ in_array_first_element,
+ in_array,
+ in_object_first_key,
+ in_object_key,
+ in_object_val
+ } state = empty;
+
+ std::stack<write_state> old_states;
+
+ void maybeComma()
+ {
+ switch (state)
+ {
+ case empty:
+ break;
+ case in_object_val:
+ state = in_object_key;
+ break;
+ case in_object_key:
+ state = in_object_val;
+ os << ',';
+ break;
+ case in_array:
+ os << ',';
+ break;
+ case in_array_first_element:
+ state = in_array;
+ break;
+ case in_object_first_key:
+ state = in_object_val;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ void pushState(write_state state)
+ {
+ old_states.push(this->state);
+ this->state = state;
+ }
+
+ void popState()
+ {
+ this->state = old_states.top();
+ old_states.pop();
+ }
+
+ public:
+ explicit JsonWriter(std::ostream &os) : os(os), old_locale{os.imbue(std::locale::classic())} {}
+ ~JsonWriter() { os.imbue(old_locale); }
+ JsonWriter &beginArray()
+ {
+ maybeComma();
+ pushState(in_array_first_element);
+ os << '[';
+ return *this;
+ }
+ JsonWriter &endArray()
+ {
+ popState();
+ os << ']';
+ return *this;
+ }
+ JsonWriter &beginObject()
+ {
+ maybeComma();
+ pushState(in_object_first_key);
+ os << '{';
+ return *this;
+ }
+ JsonWriter &endObject()
+ {
+ popState();
+ os << '}';
+ return *this;
+ }
+ std::ostream &encodeString(std::ostream &out, std::string const &str)
+ {
+ out << '"';
+
+ for (std::string::const_iterator c = str.begin(); c != str.end(); c++)
+ {
+ if (*c <= 0x1F || *c == '"' || *c == '\\')
+ ioprintf(out, "\\u%04X", *c);
+ else
+ out << *c;
+ }
+
+ out << '"';
+ return out;
+ }
+ JsonWriter &name(std::string const &name)
+ {
+ maybeComma();
+ encodeString(os, name) << ':';
+ return *this;
+ }
+ JsonWriter &value(std::string const &value)
+ {
+ maybeComma();
+ encodeString(os, value);
+ return *this;
+ }
+ JsonWriter &value(const char *value)
+ {
+ maybeComma();
+ if (value == nullptr)
+ os << "null";
+ else
+ encodeString(os, value);
+ return *this;
+ }
+ JsonWriter &value(int value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+ JsonWriter &value(long value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+ JsonWriter &value(long long value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+ JsonWriter &value(unsigned long long value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+ JsonWriter &value(unsigned long value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+ JsonWriter &value(unsigned int value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+ JsonWriter &value(bool value)
+ {
+ maybeComma();
+ os << (value ? "true" : "false");
+ return *this;
+ }
+ JsonWriter &value(double value)
+ {
+ maybeComma();
+ os << value;
+ return *this;
+ }
+};
+
+/**
+ * @brief Write a VerIterator to a JsonWriter
+ */
+static void verIterToJson(JsonWriter &writer, CacheFile &Cache, pkgCache::VerIterator const &Ver)
+{
+ writer.beginObject();
+ writer.name("id").value(Ver->ID);
+ writer.name("version").value(Ver.VerStr());
+ writer.name("architecture").value(Ver.Arch());
+ writer.name("pin").value(Cache->GetPolicy().GetPriority(Ver));
+ writer.endObject();
+}
+
+/**
+ * @brief Copy of debSystem::DpkgChrootDirectory()
+ * @todo Remove
+ */
+static void DpkgChrootDirectory()
+{
+ std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
+ if (chrootDir == "/")
+ return;
+ std::cerr << "Chrooting into " << chrootDir << std::endl;
+ if (chroot(chrootDir.c_str()) != 0)
+ _exit(100);
+ if (chdir("/") != 0)
+ _exit(100);
+}
+
+/**
+ * @brief Send a notification to the hook's stream
+ */
+static void NotifyHook(std::ostream &os, std::string const &method, const char **FileList, CacheFile &Cache, std::set<std::string> const &UnknownPackages)
+{
+ SortedPackageUniverse Universe(Cache);
+ JsonWriter jsonWriter{os};
+
+ jsonWriter.beginObject();
+
+ jsonWriter.name("jsonrpc").value("2.0");
+ jsonWriter.name("method").value(method);
+
+ /* Build params */
+ jsonWriter.name("params").beginObject();
+ jsonWriter.name("command").value(FileList[0]);
+ jsonWriter.name("search-terms").beginArray();
+ for (int i = 1; FileList[i] != NULL; i++)
+ jsonWriter.value(FileList[i]);
+ jsonWriter.endArray();
+ jsonWriter.name("unknown-packages").beginArray();
+ for (auto const &PkgName : UnknownPackages)
+ jsonWriter.value(PkgName);
+ jsonWriter.endArray();
+
+ jsonWriter.name("packages").beginArray();
+ for (auto const &Pkg : Universe)
+ {
+ switch (Cache[Pkg].Mode)
+ {
+ case pkgDepCache::ModeInstall:
+ case pkgDepCache::ModeDelete:
+ break;
+ default:
+ continue;
+ }
+
+ jsonWriter.beginObject();
+
+ jsonWriter.name("id").value(Pkg->ID);
+ jsonWriter.name("name").value(Pkg.Name());
+ jsonWriter.name("architecture").value(Pkg.Arch());
+
+ switch (Cache[Pkg].Mode)
+ {
+ case pkgDepCache::ModeInstall:
+ jsonWriter.name("mode").value("install");
+ break;
+ case pkgDepCache::ModeDelete:
+ jsonWriter.name("mode").value(Cache[Pkg].Purge() ? "purge" : "deinstall");
+ break;
+ default:
+ continue;
+ }
+ jsonWriter.name("automatic").value(bool(Cache[Pkg].Flags & pkgCache::Flag::Auto));
+
+ jsonWriter.name("versions").beginObject();
+
+ if (Cache[Pkg].CandidateVer != nullptr)
+ verIterToJson(jsonWriter.name("candidate"), Cache, Cache[Pkg].CandidateVerIter(Cache));
+ if (Cache[Pkg].InstallVer != nullptr)
+ verIterToJson(jsonWriter.name("install"), Cache, Cache[Pkg].InstVerIter(Cache));
+ if (Pkg->CurrentVer != 0)
+ verIterToJson(jsonWriter.name("current"), Cache, Pkg.CurrentVer());
+
+ jsonWriter.endObject();
+
+ jsonWriter.endObject();
+ }
+
+ jsonWriter.endArray(); // packages
+ jsonWriter.endObject(); // params
+ jsonWriter.endObject(); // main
+}
+
+/// @brief Build the hello handshake message for 0.1 protocol
+static std::string BuildHelloMessage()
+{
+ std::stringstream Hello;
+ JsonWriter(Hello).beginObject().name("jsonrpc").value("2.0").name("method").value("org.debian.apt.hooks.hello").name("id").value(0).name("params").beginObject().name("versions").beginArray().value("0.1").endArray().endObject().endObject();
+
+ return Hello.str();
+}
+
+/// @brief Build the bye notification for 0.1 protocol
+static std::string BuildByeMessage()
+{
+ std::stringstream Bye;
+ JsonWriter(Bye).beginObject().name("jsonrpc").value("2.0").name("method").value("org.debian.apt.hooks.bye").name("params").beginObject().endObject().endObject();
+
+ return Bye.str();
+}
+
+/// @brief Run the Json hook processes in the given option.
+bool RunJsonHook(std::string const &option, std::string const &method, const char **FileList, CacheFile &Cache, std::set<std::string> const &UnknownPackages)
+{
+ std::stringstream ss;
+ NotifyHook(ss, method, FileList, Cache, UnknownPackages);
+ std::string TheData = ss.str();
+ std::string HelloData = BuildHelloMessage();
+ std::string ByeData = BuildByeMessage();
+
+ bool result = true;
+
+ Configuration::Item const *Opts = _config->Tree(option.c_str());
+ if (Opts == 0 || Opts->Child == 0)
+ return true;
+ Opts = Opts->Child;
+
+ // Flush output before calling hooks
+ std::clog.flush();
+ std::cerr.flush();
+ std::cout.flush();
+ c2out.flush();
+ c1out.flush();
+
+ sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN);
+ sighandler_t old_sigint = signal(SIGINT, SIG_IGN);
+ sighandler_t old_sigquit = signal(SIGQUIT, SIG_IGN);
+
+ unsigned int Count = 1;
+ for (; Opts != 0; Opts = Opts->Next, Count++)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+
+ if (_config->FindB("Debug::RunScripts", false) == true)
+ std::clog << "Running external script with list of all .deb file: '"
+ << Opts->Value << "'" << std::endl;
+
+ // Create the pipes
+ std::set<int> KeepFDs;
+ MergeKeepFdsFromConfiguration(KeepFDs);
+ int Pipes[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, Pipes) != 0)
+ {
+ result = _error->Errno("pipe", "Failed to create IPC pipe to subprocess");
+ break;
+ }
+
+ int InfoFD = 3;
+
+ if (InfoFD != Pipes[0])
+ SetCloseExec(Pipes[0], true);
+ else
+ KeepFDs.insert(Pipes[0]);
+
+ SetCloseExec(Pipes[1], true);
+
+ // Purified Fork for running the script
+ pid_t Process = ExecFork(KeepFDs);
+ if (Process == 0)
+ {
+ // Setup the FDs
+ dup2(Pipes[0], InfoFD);
+ SetCloseExec(STDOUT_FILENO, false);
+ SetCloseExec(STDIN_FILENO, false);
+ SetCloseExec(STDERR_FILENO, false);
+
+ std::string hookfd;
+ strprintf(hookfd, "%d", InfoFD);
+ setenv("APT_HOOK_SOCKET", hookfd.c_str(), 1);
+
+ DpkgChrootDirectory();
+ const char *Args[4];
+ Args[0] = "/bin/sh";
+ Args[1] = "-c";
+ Args[2] = Opts->Value.c_str();
+ Args[3] = 0;
+ execv(Args[0], (char **)Args);
+ _exit(100);
+ }
+ close(Pipes[0]);
+ FILE *F = fdopen(Pipes[1], "w+");
+ if (F == 0)
+ {
+ result = _error->Errno("fdopen", "Failed to open new FD");
+ break;
+ }
+
+ fwrite(HelloData.data(), HelloData.size(), 1, F);
+ fwrite("\n\n", 2, 1, F);
+ fflush(F);
+
+ char *line = nullptr;
+ size_t linesize = 0;
+ ssize_t size = getline(&line, &linesize, F);
+
+ if (size < 0)
+ {
+ if (errno != ECONNRESET && errno != EPIPE)
+ _error->Error("Could not read response to hello message from hook %s: %s", Opts->Value.c_str(), strerror(errno));
+ goto out;
+ }
+ else if (strstr(line, "error") != nullptr)
+ {
+ _error->Error("Hook %s reported an error during hello: %s", Opts->Value.c_str(), line);
+ goto out;
+ }
+
+ size = getline(&line, &linesize, F);
+ if (size < 0)
+ {
+ _error->Error("Could not read message separator line after handshake from %s: %s", Opts->Value.c_str(), feof(F) ? "end of file" : strerror(errno));
+ goto out;
+ }
+ else if (size == 0 || line[0] != '\n')
+ {
+ _error->Error("Expected empty line after handshake from %s, received %s", Opts->Value.c_str(), line);
+ goto out;
+ }
+
+ fwrite(TheData.data(), TheData.size(), 1, F);
+ fwrite("\n\n", 2, 1, F);
+
+ fwrite(ByeData.data(), ByeData.size(), 1, F);
+ fwrite("\n\n", 2, 1, F);
+ out:
+ fclose(F);
+ // Clean up the sub process
+ if (ExecWait(Process, Opts->Value.c_str()) == false)
+ {
+ result = _error->Error("Failure running hook %s", Opts->Value.c_str());
+ break;
+ }
+ }
+ signal(SIGINT, old_sigint);
+ signal(SIGPIPE, old_sigpipe);
+ signal(SIGQUIT, old_sigquit);
+
+ return result;
+}
diff --git a/apt-private/private-json-hooks.h b/apt-private/private-json-hooks.h
new file mode 100644
index 0000000..41be295
--- /dev/null
+++ b/apt-private/private-json-hooks.h
@@ -0,0 +1,14 @@
+/*
+ * private-json-hooks.h - 2nd generation, JSON-RPC, hooks for APT
+ *
+ * Copyright (c) 2018 Canonical Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <set>
+#include <string>
+
+#include <apt-private/private-cachefile.h>
+
+bool RunJsonHook(std::string const &option, std::string const &method, const char **FileList, CacheFile &Cache, std::set<std::string> const &UnknownPackages = {});
diff --git a/apt-private/private-list.cc b/apt-private/private-list.cc
new file mode 100644
index 0000000..eee657c
--- /dev/null
+++ b/apt-private/private-list.cc
@@ -0,0 +1,165 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cachefilter.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-list.h>
+#include <apt-private/private-output.h>
+
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <apti18n.h>
+ /*}}}*/
+
+struct PackageSortAlphabetic /*{{{*/
+{
+ bool operator () (const pkgCache::PkgIterator &p_lhs,
+ const pkgCache::PkgIterator &p_rhs)
+ {
+ const std::string &l_name = p_lhs.FullName(true);
+ const std::string &r_name = p_rhs.FullName(true);
+ return (l_name < r_name);
+ }
+};
+
+class PackageNameMatcher : public Matcher
+{
+ static constexpr const char *const isfnmatch_strict = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.:*";
+ pkgCacheFile &cacheFile;
+ public:
+ explicit PackageNameMatcher(pkgCacheFile &cacheFile, const char **patterns)
+ : cacheFile(cacheFile)
+ {
+ for(int i=0; patterns[i] != NULL; ++i)
+ {
+ std::string pattern = patterns[i];
+ APT::CacheFilter::Matcher *cachefilter = NULL;
+ if(_config->FindB("APT::Cmd::Use-Regexp", false) == true)
+ cachefilter = new APT::CacheFilter::PackageNameMatchesRegEx(pattern);
+ else if (pattern.find_first_not_of(isfnmatch_strict) == std::string::npos)
+ cachefilter = new APT::CacheFilter::PackageNameMatchesFnmatch(pattern);
+ else
+ cachefilter = APT::CacheFilter::ParsePattern(pattern, &cacheFile).release();
+
+ if (cachefilter == nullptr) {
+ return;
+ filters.clear();
+ }
+ filters.push_back(cachefilter);
+ }
+ }
+ virtual ~PackageNameMatcher()
+ {
+ for(J=filters.begin(); J != filters.end(); ++J)
+ delete *J;
+ }
+ virtual bool operator () (const pkgCache::PkgIterator &P) APT_OVERRIDE
+ {
+ for(J=filters.begin(); J != filters.end(); ++J)
+ {
+ APT::CacheFilter::Matcher *cachefilter = *J;
+ if((*cachefilter)(P))
+ return true;
+ }
+ return false;
+ }
+
+private:
+ std::vector<APT::CacheFilter::Matcher*> filters;
+ std::vector<APT::CacheFilter::Matcher*>::const_iterator J;
+ #undef PackageMatcher
+};
+ /*}}}*/
+static void ListAllVersions(pkgCacheFile &CacheFile, pkgRecords &records,/*{{{*/
+ pkgCache::PkgIterator const &P, std::ostream &outs,
+ std::string const &format)
+{
+ for (pkgCache::VerIterator Ver = P.VersionList();
+ Ver.end() == false; ++Ver)
+ {
+ ListSingleVersion(CacheFile, records, Ver, outs, format);
+ outs << std::endl;
+ }
+}
+ /*}}}*/
+// list - list package based on criteria /*{{{*/
+// ---------------------------------------------------------------------
+bool DoList(CommandLine &Cmd)
+{
+ pkgCacheFile CacheFile;
+ pkgCache * const Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == nullptr || CacheFile.GetDepCache() == nullptr))
+ return false;
+ pkgRecords records(CacheFile);
+
+ const char **patterns;
+ const char *all_pattern[] = { "*", NULL};
+
+ if (strv_length(Cmd.FileList + 1) == 0)
+ {
+ patterns = all_pattern;
+ } else {
+ patterns = Cmd.FileList + 1;
+ }
+
+ std::string format = "${color:highlight}${Package}${color:neutral}/${Origin} ${Version} ${Architecture}${ }${apt:Status}";
+ if (_config->FindB("APT::Cmd::List-Include-Summary", false) == true)
+ format += "\n ${Description}\n";
+
+ PackageNameMatcher matcher(CacheFile, patterns);
+ LocalitySortedVersionSet bag;
+ OpTextProgress progress(*_config);
+ progress.OverallProgress(0,
+ Cache->Head().PackageCount,
+ Cache->Head().PackageCount,
+ _("Listing"));
+ GetLocalitySortedVersionSet(CacheFile, &bag, matcher, &progress);
+ bool const ShowAllVersions = _config->FindB("APT::Cmd::All-Versions", false);
+ std::map<std::string, std::string> output_map;
+ for (LocalitySortedVersionSet::iterator V = bag.begin(); V != bag.end(); ++V)
+ {
+ std::stringstream outs;
+ if(ShowAllVersions == true)
+ ListAllVersions(CacheFile, records, V.ParentPkg(), outs, format);
+ else
+ ListSingleVersion(CacheFile, records, V, outs, format);
+ output_map.insert(std::make_pair<std::string, std::string>(
+ V.ParentPkg().FullName(), outs.str()));
+ }
+
+ // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
+ // output the sorted map
+ std::map<std::string, std::string>::const_iterator K;
+ for (K = output_map.begin(); K != output_map.end(); ++K)
+ std::cout << (*K).second << std::endl;
+
+ // be nice and tell the user if there is more to see
+ if (bag.size() == 1 && ShowAllVersions == false)
+ {
+ // start with -1 as we already displayed one version
+ int versions = -1;
+ pkgCache::VerIterator Ver = *bag.begin();
+ for ( ; Ver.end() == false; ++Ver)
+ ++versions;
+ if (versions > 0)
+ _error->Notice(P_("There is %i additional version. Please use the '-a' switch to see it", "There are %i additional versions. Please use the '-a' switch to see them.", versions), versions);
+ }
+
+ return true;
+}
+
diff --git a/apt-private/private-list.h b/apt-private/private-list.h
new file mode 100644
index 0000000..aa26777
--- /dev/null
+++ b/apt-private/private-list.h
@@ -0,0 +1,11 @@
+#ifndef APT_PRIVATE_LIST_H
+#define APT_PRIVATE_LIST_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool DoList(CommandLine &Cmd);
+
+
+#endif
diff --git a/apt-private/private-main.cc b/apt-private/private-main.cc
new file mode 100644
index 0000000..d4ce0ab
--- /dev/null
+++ b/apt-private/private-main.cc
@@ -0,0 +1,90 @@
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-main.h>
+
+#include <iostream>
+#include <locale>
+
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+
+void InitLocale(APT_CMD const binary) /*{{{*/
+{
+ try {
+ std::locale::global(std::locale(""));
+ } catch (...) {
+ setlocale(LC_ALL, "");
+ }
+ switch(binary)
+ {
+ case APT_CMD::APT:
+ case APT_CMD::APT_CACHE:
+ case APT_CMD::APT_CDROM:
+ case APT_CMD::APT_CONFIG:
+ case APT_CMD::APT_DUMP_SOLVER:
+ case APT_CMD::APT_HELPER:
+ case APT_CMD::APT_GET:
+ case APT_CMD::APT_MARK:
+ case APT_CMD::RRED:
+ textdomain("apt");
+ break;
+ case APT_CMD::APT_EXTRACTTEMPLATES:
+ case APT_CMD::APT_FTPARCHIVE:
+ case APT_CMD::APT_INTERNAL_PLANNER:
+ case APT_CMD::APT_INTERNAL_SOLVER:
+ case APT_CMD::APT_SORTPKG:
+ textdomain("apt-utils");
+ break;
+ }
+}
+ /*}}}*/
+void InitSignals() /*{{{*/
+{
+ signal(SIGPIPE,SIG_IGN);
+}
+ /*}}}*/
+void CheckIfSimulateMode(CommandLine &CmdL) /*{{{*/
+{
+ // disable locking in simulation, but show the message only for users
+ // as root hasn't the same problems like unreadable files which can heavily
+ // distort the simulation.
+ if (_config->FindB("APT::Get::Simulate") == true &&
+ (CmdL.FileSize() == 0 ||
+ (strcmp(CmdL.FileList[0], "source") != 0 && strcmp(CmdL.FileList[0], "download") != 0 &&
+ strcmp(CmdL.FileList[0], "changelog") != 0)))
+ {
+ if (getuid() != 0 && _config->FindB("APT::Get::Show-User-Simulation-Note",true) == true)
+ // TRANSLATORS: placeholder is a binary name like apt or apt-get
+ ioprintf(std::cout, _("NOTE: This is only a simulation!\n"
+ " %s needs root privileges for real execution.\n"
+ " Keep also in mind that locking is deactivated,\n"
+ " so don't depend on the relevance to the real current situation!\n"),
+ _config->Find("Binary").c_str());
+ _config->Set("Debug::NoLocking",true);
+ }
+}
+ /*}}}*/
+void CheckIfCalledByScript(int argc, const char *argv[]) /*{{{*/
+{
+ if (unlikely(argc < 1)) return;
+
+ if(!isatty(STDOUT_FILENO) &&
+ _config->FindB("Apt::Cmd::Disable-Script-Warning", false) == false)
+ {
+ std::cerr << std::endl
+ << "WARNING: " << flNotDir(argv[0]) << " "
+ << "does not have a stable CLI interface. "
+ << "Use with caution in scripts."
+ << std::endl
+ << std::endl;
+ }
+}
+ /*}}}*/
diff --git a/apt-private/private-main.h b/apt-private/private-main.h
new file mode 100644
index 0000000..4dcb271
--- /dev/null
+++ b/apt-private/private-main.h
@@ -0,0 +1,15 @@
+#ifndef APT_PRIVATE_MAIN_H
+#define APT_PRIVATE_MAIN_H
+
+#include <apt-private/private-cmndline.h>
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+void InitLocale(APT_CMD const binary);
+APT_PUBLIC void InitSignals();
+APT_PUBLIC void CheckIfSimulateMode(CommandLine &CmdL);
+APT_PUBLIC void CheckIfCalledByScript(int argc, const char *argv[]);
+
+#endif
diff --git a/apt-private/private-moo.cc b/apt-private/private-moo.cc
new file mode 100644
index 0000000..ca456ed
--- /dev/null
+++ b/apt-private/private-moo.cc
@@ -0,0 +1,200 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Here be cows – but: Never ask, never tell
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-moo.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-utils.h>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <stddef.h>
+#include <string.h>
+#include <time.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static std::string getMooLine(time_t const timenow) /*{{{*/
+{
+ struct tm special;
+ localtime_r(&timenow, &special);
+ enum { NORMAL, PACKAGEMANAGER, APPRECIATION, AGITATION, AIRBORN } line;
+ if (special.tm_mon == 11 && special.tm_mday == 25)
+ line = PACKAGEMANAGER;
+ else if (special.tm_mon == 7 && special.tm_mday == 16)
+ line = APPRECIATION;
+ else if (special.tm_mon == 10 && special.tm_mday == 7)
+ line = AGITATION;
+ else if (special.tm_mon == 1 && special.tm_mday == 18)
+ line = AIRBORN;
+ else
+ line = NORMAL;
+
+ bool const quiet = _config->FindI("quiet") >= 2;
+ std::ostringstream out;
+ if (quiet == false)
+ out << "...\"";
+
+ switch(line)
+ {
+ case PACKAGEMANAGER: out << "Happy package management day!"; break;
+ case APPRECIATION: out << "Three moos for Debian!"; break;
+ case AGITATION: out << "Whoever needs milk, bows to the animal."; break;
+ case AIRBORN: out << "It's a Bird ... It's a Plane ... It's Super Cow!"; break;
+ default: out << "Have you mooed today?"; break;
+ }
+
+ if (quiet == true)
+ out << std::endl;
+ else
+ out << "\"..." << std::endl;
+
+ return out.str();
+}
+ /*}}}*/
+static bool printMooLine(time_t const timenow) /*{{{*/
+{
+ std::cerr << getMooLine(timenow);
+ return true;
+}
+ /*}}}*/
+static bool DoMoo1(time_t const timenow) /*{{{*/
+{
+ // our trustworthy super cow since 2001
+ if (_config->FindI("quiet") >= 2)
+ return printMooLine(timenow);
+ std::string const moo = getMooLine(timenow);
+ size_t const depth = moo.length()/4;
+ c1out <<
+ OutputInDepth(depth, " ") << " (__) \n" <<
+ OutputInDepth(depth, " ") << " (oo) \n" <<
+ OutputInDepth(depth, " ") << " /------\\/ \n" <<
+ OutputInDepth(depth, " ") << " / | || \n" <<
+ OutputInDepth(depth, " ") << " * /\\---/\\ \n" <<
+ OutputInDepth(depth, " ") << " ~~ ~~ \n" <<
+ moo;
+ return true;
+}
+ /*}}}*/
+static bool DoMoo2(time_t const timenow) /*{{{*/
+{
+ // by Fernando Ribeiro in lp:56125
+ if (_config->FindI("quiet") >= 2)
+ return printMooLine(timenow);
+ std::string const moo = getMooLine(timenow);
+ size_t const depth = moo.length()/4;
+ if (_config->FindB("APT::Moo::Color", false) == false)
+ c1out <<
+ OutputInDepth(depth, " ") << " (__) \n" <<
+ OutputInDepth(depth, " ") << " _______~(..)~ \n" <<
+ OutputInDepth(depth, " ") << " ,----\\(oo) \n" <<
+ OutputInDepth(depth, " ") << " /|____|,' \n" <<
+ OutputInDepth(depth, " ") << " * /\"\\ /\\ \n" <<
+ OutputInDepth(depth, " ") << " ~ ~ ~ ~ \n" <<
+ moo;
+ else
+ {
+ c1out <<
+ OutputInDepth(depth, " ") << " \033[1;97m(\033[0;33m__\033[1;97m)\033[0m\n" <<
+ OutputInDepth(depth, " ") << " \033[31m_______\033[33m~(\033[1;34m..\033[0;33m)~\033[0m\n" <<
+ OutputInDepth(depth, " ") << " \033[33m,----\033[31m\\\033[33m(\033[1;4;35moo\033[0;33m)\033[0m\n" <<
+ OutputInDepth(depth, " ") << " \033[33m/|____|,'\033[0m\n" <<
+ OutputInDepth(depth, " ") << " \033[1;5;97m*\033[0;33m /\\ /\\\033[0m\n" <<
+ "\033[32m";
+ for (size_t i = moo.length()/2; i > 1; --i)
+ c1out << "wW";
+
+ c1out << "w\033[0m\n" << moo;
+ }
+
+ return true;
+}
+ /*}}}*/
+static bool DoMoo3(time_t const timenow) /*{{{*/
+{
+ // by Robert Millan in deb:134156
+ if (_config->FindI("quiet") >= 2)
+ return printMooLine(timenow);
+ std::string const moo = getMooLine(timenow);
+ size_t const depth = moo.length()/16;
+ c1out <<
+ OutputInDepth(depth, " ") << " \\_/ \n" <<
+ OutputInDepth(depth, " ") << " m00h (__) -(_)- \n" <<
+ OutputInDepth(depth, " ") << " \\ ~Oo~___ / \\\n" <<
+ OutputInDepth(depth, " ") << " (..) |\\ \n" <<
+ OutputInDepth(depth, "_") << "_________|_|_|__________" <<
+ OutputInDepth((moo.length() - (depth + 27)), "_") << "\n" << moo;
+ return true;
+}
+ /*}}}*/
+static bool DoMooApril() /*{{{*/
+{
+ // by Christopher Allan Webber and proposed by Paul Tagliamonte
+ // in a "Community outreach": https://lists.debian.org/debian-devel/2013/04/msg00045.html
+ if (_config->FindI("quiet") >= 2)
+ {
+ std::cerr << "Have you smashed some milk today?" << std::endl;
+ return true;
+ }
+ c1out <<
+ " _ _\n"
+ " (_\\___( \\,\n"
+ " )___ _ Have you smashed some milk today?\n"
+ " /( (_)-(_) /\n"
+ " ,---------' \\_\n"
+ " //( ',__,' \\ (' ')\n"
+ " // ) '----'\n"
+ " '' ; \\ .--. ,/\n"
+ " | )',_,'----( ;\n"
+ " ||| ''' '||\n";
+ return true;
+}
+ /*}}}*/
+bool DoMoo(CommandLine &CmdL) /*{{{*/
+{
+ time_t const timenow = GetSecondsSinceEpoch();
+
+ struct tm april;
+ localtime_r(&timenow, &april);
+ if (april.tm_mday == 1 && april.tm_mon == 3)
+ return DoMooApril();
+
+ signed short SuperCow = 1;
+ if (CmdL.FileSize() != 0)
+ for (const char **Moo = CmdL.FileList + 1; *Moo != 0; Moo++)
+ if (strcasecmp(*Moo, "moo") == 0)
+ SuperCow++;
+
+ // time is random enough for our purpose
+ if (SuperCow > 3)
+ {
+ if (april.tm_sec == 1)
+ SuperCow = 1 + (timenow % 4);
+ else
+ SuperCow = 1 + (timenow % 3);
+ }
+
+ switch(SuperCow) {
+ case 1: return DoMoo1(timenow);
+ case 2: return DoMoo2(timenow);
+ case 3: return DoMoo3(timenow);
+ case 4: return DoMooApril();
+ default: return DoMoo1(timenow);
+ }
+
+ return true;
+}
+ /*}}}*/
diff --git a/apt-private/private-moo.h b/apt-private/private-moo.h
new file mode 100644
index 0000000..c230ce2
--- /dev/null
+++ b/apt-private/private-moo.h
@@ -0,0 +1,10 @@
+#ifndef APT_PRIVATE_MOO_H
+#define APT_PRIVATE_MOO_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool DoMoo(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc
new file mode 100644
index 0000000..eb9a34a
--- /dev/null
+++ b/apt-private/private-output.cc
@@ -0,0 +1,801 @@
+// Include files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-output.h>
+
+#include <iomanip>
+#include <iostream>
+#include <langinfo.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <sstream>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+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(std::basic_streambuf<char> * const out) /*{{{*/
+{
+ if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
+ _config->Set("quiet","1");
+
+ 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)
+ c1out.rdbuf(devnull.rdbuf());
+
+ // deal with window size changes
+ auto cols = getenv("COLUMNS");
+ if (cols != nullptr)
+ {
+ char * colends;
+ auto const sw = strtoul(cols, &colends, 10);
+ if (*colends != '\0' || sw == 0)
+ {
+ _error->Warning("Environment variable COLUMNS was ignored as it has an invalid value: \"%s\"", cols);
+ cols = nullptr;
+ }
+ else
+ ScreenWidth = sw;
+ }
+ if (cols == nullptr)
+ {
+ signal(SIGWINCH,SigWinch);
+ SigWinch(0);
+ }
+
+ if(!isatty(1))
+ {
+ _config->Set("APT::Color", "false");
+ _config->Set("APT::Color::Highlight", "");
+ _config->Set("APT::Color::Neutral", "");
+ } else {
+ // Colors
+ _config->CndSet("APT::Color::Highlight", "\x1B[32m");
+ _config->CndSet("APT::Color::Neutral", "\x1B[0m");
+
+ _config->CndSet("APT::Color::Red", "\x1B[31m");
+ _config->CndSet("APT::Color::Green", "\x1B[32m");
+ _config->CndSet("APT::Color::Yellow", "\x1B[33m");
+ _config->CndSet("APT::Color::Blue", "\x1B[34m");
+ _config->CndSet("APT::Color::Magenta", "\x1B[35m");
+ _config->CndSet("APT::Color::Cyan", "\x1B[36m");
+ _config->CndSet("APT::Color::White", "\x1B[37m");
+ }
+
+ return true;
+}
+ /*}}}*/
+static std::string GetArchiveSuite(pkgCacheFile &/*CacheFile*/, pkgCache::VerIterator ver) /*{{{*/
+{
+ std::string suite = "";
+ if (ver && ver.FileList())
+ {
+ pkgCache::VerFileIterator VF = ver.FileList();
+ for (; VF.end() == false ; ++VF)
+ {
+ if(VF.File() == NULL || VF.File().Archive() == NULL)
+ suite = suite + "," + _("unknown");
+ else
+ suite = suite + "," + VF.File().Archive();
+ //suite = VF.File().Archive();
+ }
+ suite = suite.erase(0, 1);
+ }
+ return suite;
+}
+ /*}}}*/
+static std::string GetFlagsStr(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+{
+ pkgDepCache *DepCache = CacheFile.GetDepCache();
+ pkgDepCache::StateCache &state = (*DepCache)[P];
+
+ std::string flags_str;
+ if (state.NowBroken())
+ flags_str = "B";
+ if (P.CurrentVer() && state.Upgradable() && state.CandidateVer != NULL)
+ flags_str = "g";
+ else if (P.CurrentVer() != NULL)
+ flags_str = "i";
+ else
+ flags_str = "-";
+ return flags_str;
+}
+ /*}}}*/
+static std::string GetCandidateVersion(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+{
+ pkgPolicy *policy = CacheFile.GetPolicy();
+ pkgCache::VerIterator cand = policy->GetCandidateVer(P);
+
+ return cand ? cand.VerStr() : "(none)";
+}
+ /*}}}*/
+static std::string GetInstalledVersion(pkgCacheFile &/*CacheFile*/, pkgCache::PkgIterator P)/*{{{*/
+{
+ pkgCache::VerIterator inst = P.CurrentVer();
+
+ return inst ? inst.VerStr() : "(none)";
+}
+ /*}}}*/
+static std::string GetVersion(pkgCacheFile &/*CacheFile*/, pkgCache::VerIterator V)/*{{{*/
+{
+ pkgCache::PkgIterator P = V.ParentPkg();
+ if (V == P.CurrentVer())
+ {
+ std::string inst_str = DeNull(V.VerStr());
+#if 0 // FIXME: do we want this or something like this?
+ pkgDepCache *DepCache = CacheFile.GetDepCache();
+ pkgDepCache::StateCache &state = (*DepCache)[P];
+ if (state.Upgradable())
+ return "**"+inst_str;
+#endif
+ return inst_str;
+ }
+
+ if(V)
+ return DeNull(V.VerStr());
+ return "(none)";
+}
+ /*}}}*/
+static std::string GetArchitecture(pkgCacheFile &CacheFile, pkgCache::PkgIterator P)/*{{{*/
+{
+ if (P->CurrentVer == 0)
+ {
+ pkgDepCache * const DepCache = CacheFile.GetDepCache();
+ pkgDepCache::StateCache const &state = (*DepCache)[P];
+ if (state.CandidateVer != NULL)
+ {
+ pkgCache::VerIterator const CandV(CacheFile, state.CandidateVer);
+ return CandV.Arch();
+ }
+ else
+ {
+ pkgCache::VerIterator const V = P.VersionList();
+ if (V.end() == false)
+ return V.Arch();
+ else
+ return P.Arch();
+ }
+ }
+ else
+ return P.CurrentVer().Arch();
+}
+ /*}}}*/
+static std::string GetShortDescription(pkgCacheFile &CacheFile, pkgRecords &records, pkgCache::PkgIterator P)/*{{{*/
+{
+ pkgPolicy *policy = CacheFile.GetPolicy();
+
+ pkgCache::VerIterator ver;
+ if (P.CurrentVer())
+ ver = P.CurrentVer();
+ else
+ ver = policy->GetCandidateVer(P);
+
+ std::string ShortDescription = "(none)";
+ if(ver)
+ {
+ pkgCache::DescIterator const Desc = ver.TranslatedDescription();
+ if (Desc.end() == false)
+ {
+ pkgRecords::Parser & parser = records.Lookup(Desc.FileList());
+ ShortDescription = parser.ShortDesc();
+ }
+ }
+ return ShortDescription;
+}
+ /*}}}*/
+static std::string GetLongDescription(pkgCacheFile &CacheFile, pkgRecords &records, pkgCache::PkgIterator P)/*{{{*/
+{
+ pkgPolicy *policy = CacheFile.GetPolicy();
+
+ pkgCache::VerIterator ver;
+ if (P->CurrentVer != 0)
+ ver = P.CurrentVer();
+ else
+ ver = policy->GetCandidateVer(P);
+
+ std::string const EmptyDescription = "(none)";
+ if(ver.end() == true)
+ return EmptyDescription;
+
+ pkgCache::DescIterator const Desc = ver.TranslatedDescription();
+ 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, /*{{{*/
+ pkgCache::VerIterator const &V, std::ostream &out,
+ std::string const &format)
+{
+ pkgCache::PkgIterator const P = V.ParentPkg();
+ pkgDepCache * const DepCache = CacheFile.GetDepCache();
+ pkgDepCache::StateCache const &state = (*DepCache)[P];
+
+ std::string output;
+ if (_config->FindB("APT::Cmd::use-format", false))
+ output = _config->Find("APT::Cmd::format", "${db::Status-Abbrev} ${Package} ${Version} ${Origin} ${Description}");
+ else
+ output = format;
+
+ // FIXME: some of these names are really icky – and all is nowhere documented
+ output = SubstVar(output, "${db::Status-Abbrev}", GetFlagsStr(CacheFile, P));
+ output = SubstVar(output, "${Package}", P.Name());
+ std::string const ArchStr = GetArchitecture(CacheFile, P);
+ output = SubstVar(output, "${Architecture}", ArchStr);
+ std::string const InstalledVerStr = GetInstalledVersion(CacheFile, P);
+ output = SubstVar(output, "${installed:Version}", InstalledVerStr);
+ std::string const CandidateVerStr = GetCandidateVersion(CacheFile, P);
+ output = SubstVar(output, "${candidate:Version}", CandidateVerStr);
+ std::string const VersionStr = GetVersion(CacheFile, V);
+ output = SubstVar(output, "${Version}", VersionStr);
+ output = SubstVar(output, "${Origin}", GetArchiveSuite(CacheFile, V));
+
+ std::string StatusStr = "";
+ if (P->CurrentVer != 0)
+ {
+ if (P.CurrentVer() == V)
+ {
+ if (state.Upgradable() && state.CandidateVer != NULL)
+ strprintf(StatusStr, _("[installed,upgradable to: %s]"),
+ CandidateVerStr.c_str());
+ else if (V.Downloadable() == false)
+ StatusStr = _("[installed,local]");
+ else if(V.Automatic() == true && state.Garbage == true)
+ StatusStr = _("[installed,auto-removable]");
+ else if ((state.Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
+ StatusStr = _("[installed,automatic]");
+ else
+ StatusStr = _("[installed]");
+ }
+ else if (state.CandidateVer == V && state.Upgradable())
+ strprintf(StatusStr, _("[upgradable from: %s]"),
+ InstalledVerStr.c_str());
+ }
+ else if (V.ParentPkg()->CurrentState == pkgCache::State::ConfigFiles)
+ StatusStr = _("[residual-config]");
+ output = SubstVar(output, "${apt:Status}", StatusStr);
+ 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));
+ 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, "${ }", " ");
+ if (APT::String::Endswith(output, " ") == true)
+ output.erase(output.length() - 1);
+
+ out << output;
+}
+ /*}}}*/
+// ShowBroken - Debugging aide /*{{{*/
+// ---------------------------------------------------------------------
+/* This prints out the names of all the packages that are broken along
+ with the name of each broken dependency and a quite version
+ description.
+
+ The output looks like:
+ The following packages have unmet dependencies:
+ exim: Depends: libc6 (>= 2.1.94) but 2.1.3-10 is to be installed
+ Depends: libldap2 (>= 2.0.2-2) but it is not going to be installed
+ Depends: libsasl7 but it is not going to be installed
+ */
+static void ShowBrokenPackage(ostream &out, pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg, bool const Now)
+{
+ 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;
+ }
+
+ for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false;)
+ {
+ // 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)[End] & pkgDepCache::DepGNow) == pkgDepCache::DepGNow)
+ continue;
+ }
+ else
+ {
+ 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;
+
+ if (FirstOr == false)
+ {
+ 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)
+ {
+ out << ' ';
+ pkgCache::VerIterator Ver = (*Cache)[Targ].InstVerIter(*Cache);
+ if (Now == true)
+ Ver = Targ.CurrentVer();
+
+ if (Ver.end() == false)
+ {
+ if (Now == true)
+ ioprintf(out,_("but %s is installed"),Ver.VerStr());
+ else
+ ioprintf(out,_("but %s is to be installed"),Ver.VerStr());
+ }
+ 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"));
+ }
+ }
+
+ 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;
+ SortedPackageUniverse Universe(Cache);
+ for (auto const &Pkg: Universe)
+ ShowBrokenPackage(out, &Cache, Pkg, Now);
+}
+void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now)
+{
+ if (Cache->BrokenCount() == 0)
+ return;
+
+ out << _("The following packages have unmet dependencies:") << endl;
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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 /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool ShowDowngraded(ostream &out,CacheFile &Cache)
+{
+ 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)
+{
+ 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.
+ 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)
+{
+ std::vector<bool> Added(Cache->Head().PackageCount, false);
+ APT::PackageDeque pkglist;
+ PrettyFullNameWithDue withdue;
+
+ SortedPackageUniverse Universe(Cache);
+ for (pkgCache::PkgIterator const &I: Universe)
+ {
+ 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() == false)
+ continue;
+
+ if (Added[I->ID] == false)
+ {
+ Added[I->ID] = true;
+ pkglist.insert(I);
+ }
+
+ if (I->CurrentVer == 0)
+ continue;
+
+ // Print out any essential package depenendents that are to be removed
+ for (pkgCache::DepIterator D = I.CurrentVer().DependsList(); D.end() == false; ++D)
+ {
+ // Skip everything but depends
+ 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;
+
+ pkglist.insert(P);
+ withdue.due[P->ID] = I;
+ }
+ }
+ }
+ 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!"),
+ pkglist, &AlwaysTrue, withdue, &EmptyString);
+}
+ /*}}}*/
+// Stats - Show some statistics /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Stats(ostream &out,pkgDepCache &Dep)
+{
+ unsigned long Upgrade = 0;
+ unsigned long Downgrade = 0;
+ unsigned long Install = 0;
+ unsigned long ReInstall = 0;
+ for (pkgCache::PkgIterator I = Dep.PkgBegin(); I.end() == false; ++I)
+ {
+ if (Dep[I].NewInstall() == true)
+ Install++;
+ else
+ {
+ if (Dep[I].Upgrade() == true)
+ Upgrade++;
+ else
+ if (Dep[I].Downgrade() == true)
+ Downgrade++;
+ }
+
+ if (Dep[I].Delete() == false && (Dep[I].iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
+ ReInstall++;
+ }
+
+ ioprintf(out,_("%lu upgraded, %lu newly installed, "),
+ Upgrade,Install);
+
+ if (ReInstall != 0)
+ ioprintf(out,_("%lu reinstalled, "),ReInstall);
+ if (Downgrade != 0)
+ ioprintf(out,_("%lu downgraded, "),Downgrade);
+
+ ioprintf(out,_("%lu to remove and %lu not upgraded.\n"),
+ Dep.DelCount(),Dep.KeepCount());
+
+ if (Dep.BadCount() != 0)
+ ioprintf(out,_("%lu not fully installed or removed.\n"),
+ Dep.BadCount());
+}
+ /*}}}*/
+// YnPrompt - Yes No Prompt. /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns true on a Yes.*/
+bool YnPrompt(char const * const Question, bool const Default, bool const ShowGlobalErrors, std::ostream &c1o, std::ostream &c2o)
+{
+ 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 (ShowGlobalErrors == true && AssumeYes == false && AssumeNo == false)
+ {
+ if (_config->FindI("quiet",0) > 0)
+ _error->DumpErrors(c2o);
+ else
+ _error->DumpErrors(c2o, GlobalError::DEBUG);
+ }
+
+ c2o << 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");
+ if (language != NULL)
+ language = strdup(language);
+ if (language != NULL)
+ unsetenv("LANGUAGE");
+
+ if (Default == true)
+ // TRANSLATOR: Yes/No question help-text: defaulting to Y[es]
+ // e.g. "Do you want to continue? [Y/n] "
+ // The user has to answer with an input matching the
+ // YESEXPR/NOEXPR defined in your l10n.
+ c2o << " " << _("[Y/n]") << " " << std::flush;
+ else
+ // TRANSLATOR: Yes/No question help-text: defaulting to N[o]
+ // e.g. "Should this file be removed? [y/N] "
+ // The user has to answer with an input matching the
+ // YESEXPR/NOEXPR defined in your l10n.
+ c2o << " " << _("[y/N]") << " " << std::flush;
+
+ if (language != NULL)
+ {
+ setenv("LANGUAGE", language, 0);
+ free(language);
+ }
+
+ if (AssumeYes)
+ {
+ // TRANSLATOR: "Yes" answer printed for a yes/no question if --assume-yes is set
+ c1o << _("Y") << std::endl;
+ return true;
+ }
+ else if (AssumeNo)
+ {
+ // TRANSLATOR: "No" answer printed for a yes/no question if --assume-no is set
+ c1o << _("N") << std::endl;
+ return false;
+ }
+
+ char response[1024] = "";
+ std::cin.getline(response, sizeof(response));
+
+ if (!std::cin)
+ return false;
+
+ if (strlen(response) == 0)
+ return Default;
+
+ regex_t Pattern;
+ int Res;
+
+ Res = regcomp(&Pattern, nl_langinfo(YESEXPR),
+ REG_EXTENDED|REG_ICASE|REG_NOSUB);
+
+ if (Res != 0) {
+ char Error[300];
+ regerror(Res,&Pattern,Error,sizeof(Error));
+ return _error->Error(_("Regex compilation error - %s"),Error);
+ }
+
+ Res = regexec(&Pattern, response, 0, NULL, 0);
+ if (Res == 0)
+ return true;
+ return false;
+}
+bool YnPrompt(char const * const Question, bool const Default)
+{
+ return YnPrompt(Question, Default, true, c1out, c2out);
+}
+ /*}}}*/
+// AnalPrompt - Annoying Yes No Prompt. /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns true on a Yes.*/
+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)
+ return true;
+ 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();
+}
+
diff --git a/apt-private/private-output.h b/apt-private/private-output.h
new file mode 100644
index 0000000..c20cc9e
--- /dev/null
+++ b/apt-private/private-output.h
@@ -0,0 +1,116 @@
+#ifndef APT_PRIVATE_OUTPUT_H
+#define APT_PRIVATE_OUTPUT_H
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <string>
+
+// forward declaration
+class pkgCacheFile;
+class CacheFile;
+class pkgDepCache;
+class pkgRecords;
+
+
+APT_PUBLIC extern std::ostream c0out;
+APT_PUBLIC extern std::ostream c1out;
+APT_PUBLIC extern std::ostream c2out;
+APT_PUBLIC extern std::ofstream devnull;
+APT_PUBLIC extern unsigned int ScreenWidth;
+
+APT_PUBLIC bool InitOutput(std::basic_streambuf<char> * const out = std::cout.rdbuf());
+
+void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,
+ pkgCache::VerIterator const &V, std::ostream &out,
+ std::string const &format);
+
+
+// helper to describe global state
+APT_PUBLIC void ShowBroken(std::ostream &out, CacheFile &Cache, bool const Now);
+APT_PUBLIC void ShowBroken(std::ostream &out, pkgCacheFile &Cache, bool const Now);
+
+template<class Container, class PredicateC, class DisplayP, class DisplayV> bool ShowList(std::ostream &out, std::string const &Title,
+ Container const &cont,
+ PredicateC Predicate,
+ DisplayP PkgDisplay,
+ DisplayV VerboseDisplay)
+{
+ size_t const ScreenWidth = (::ScreenWidth > 3) ? ::ScreenWidth - 3 : 0;
+ int ScreenUsed = 0;
+ bool const ShowVersions = _config->FindB("APT::Get::Show-Versions", false);
+ bool printedTitle = false;
+
+ for (auto const &Pkg: cont)
+ {
+ if (Predicate(Pkg) == false)
+ continue;
+
+ if (printedTitle == false)
+ {
+ out << Title;
+ printedTitle = true;
+ }
+
+ if (ShowVersions == true)
+ {
+ out << std::endl << " " << PkgDisplay(Pkg);
+ std::string const verbose = VerboseDisplay(Pkg);
+ if (verbose.empty() == false)
+ out << " (" << verbose << ")";
+ }
+ else
+ {
+ std::string const PkgName = PkgDisplay(Pkg);
+ if (ScreenUsed == 0 || (ScreenUsed + PkgName.length()) >= ScreenWidth)
+ {
+ out << std::endl << " ";
+ ScreenUsed = 0;
+ }
+ else if (ScreenUsed != 0)
+ {
+ out << " ";
+ ++ScreenUsed;
+ }
+ out << PkgName;
+ ScreenUsed += PkgName.length();
+ }
+ }
+
+ if (printedTitle == true)
+ {
+ out << std::endl;
+ return false;
+ }
+ return true;
+}
+
+void ShowNew(std::ostream &out,CacheFile &Cache);
+void ShowDel(std::ostream &out,CacheFile &Cache);
+void ShowKept(std::ostream &out,CacheFile &Cache);
+void ShowUpgraded(std::ostream &out,CacheFile &Cache);
+bool ShowDowngraded(std::ostream &out,CacheFile &Cache);
+bool ShowHold(std::ostream &out,CacheFile &Cache);
+
+bool ShowEssential(std::ostream &out,CacheFile &Cache);
+
+void Stats(std::ostream &out, pkgDepCache &Dep);
+
+// prompting
+APT_PUBLIC bool YnPrompt(char const *const Question, bool Default = true);
+bool YnPrompt(char const * const Question, bool const Default, bool const ShowGlobalErrors, std::ostream &c1o, std::ostream &c2o);
+bool AnalPrompt(std::string const &Question, const char *Text);
+
+APT_PUBLIC std::string PrettyFullName(pkgCache::PkgIterator const &Pkg);
+std::string CandidateVersion(pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg);
+std::function<std::string(pkgCache::PkgIterator const &)> CandidateVersion(pkgCacheFile * const Cache);
+std::string CurrentToCandidateVersion(pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg);
+std::function<std::string(pkgCache::PkgIterator const &)> CurrentToCandidateVersion(pkgCacheFile * const Cache);
+std::string EmptyString(pkgCache::PkgIterator const &);
+bool AlwaysTrue(pkgCache::PkgIterator const &);
+
+#endif
diff --git a/apt-private/private-search.cc b/apt-private/private-search.cc
new file mode 100644
index 0000000..b211474
--- /dev/null
+++ b/apt-private/private-search.cc
@@ -0,0 +1,418 @@
+// Includes /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/progress.h>
+
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-json-hooks.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-search.h>
+#include <apt-private/private-show.h>
+
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+#include <string.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static std::vector<pkgCache::DescIterator> const TranslatedDescriptionsList(pkgCache::VerIterator const &V) /*{{{*/
+{
+ std::vector<pkgCache::DescIterator> Descriptions;
+
+ for (std::string const &lang: APT::Configuration::getLanguages())
+ {
+ pkgCache::DescIterator Desc = V.TranslatedDescriptionForLanguage(lang);
+ if (Desc.IsGood())
+ Descriptions.push_back(Desc);
+ }
+
+ if (Descriptions.empty() && V.TranslatedDescription().IsGood())
+ Descriptions.push_back(V.TranslatedDescription());
+
+ return Descriptions;
+}
+
+ /*}}}*/
+static bool FullTextSearch(CommandLine &CmdL) /*{{{*/
+{
+
+ CacheFile CacheFile;
+ CacheFile.GetDepCache();
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ pkgDepCache::Policy *Plcy = CacheFile.GetPolicy();
+ if (unlikely(Cache == NULL || Plcy == NULL))
+ return false;
+
+ // Make sure there is at least one argument
+ unsigned int const NumPatterns = CmdL.FileSize() -1;
+ if (NumPatterns < 1)
+ return _error->Error(_("You must give at least one search pattern"));
+
+ RunJsonHook("AptCli::Hooks::Search", "org.debian.apt.hooks.search.pre", CmdL.FileList, CacheFile);
+
+#define APT_FREE_PATTERNS() for (std::vector<regex_t>::iterator P = Patterns.begin(); \
+ P != Patterns.end(); ++P) { regfree(&(*P)); }
+
+ // Compile the regex pattern
+ std::vector<regex_t> Patterns;
+ for (unsigned int I = 0; I != NumPatterns; ++I)
+ {
+ regex_t pattern;
+ if (regcomp(&pattern, CmdL.FileList[I + 1], REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
+ {
+ APT_FREE_PATTERNS();
+ return _error->Error("Regex compilation error");
+ }
+ Patterns.push_back(pattern);
+ }
+
+ std::map<std::string, std::string> output_map;
+
+ LocalitySortedVersionSet bag;
+ OpTextProgress progress(*_config);
+ progress.OverallProgress(0, 100, 50, _("Sorting"));
+ GetLocalitySortedVersionSet(CacheFile, &bag, &progress);
+ LocalitySortedVersionSet::iterator V = bag.begin();
+
+ progress.OverallProgress(50, 100, 50, _("Full Text Search"));
+ progress.SubProgress(bag.size());
+ pkgRecords records(CacheFile);
+
+ std::string format = "${color:highlight}${Package}${color:neutral}/${Origin} ${Version} ${Architecture}${ }${apt:Status}\n";
+ if (_config->FindB("APT::Cache::ShowFull",false) == false)
+ format += " ${Description}\n";
+ else
+ format += " ${LongDescription}\n";
+
+ bool const NamesOnly = _config->FindB("APT::Cache::NamesOnly", false);
+ int Done = 0;
+ std::vector<bool> PkgsDone(Cache->Head().PackageCount, false);
+ for ( ;V != bag.end(); ++V)
+ {
+ if (Done%500 == 0)
+ progress.Progress(Done);
+ ++Done;
+
+ // we want to list each package only once
+ pkgCache::PkgIterator const P = V.ParentPkg();
+ if (PkgsDone[P->ID] == true)
+ continue;
+
+ std::vector<std::string> PkgDescriptions;
+ if (not NamesOnly)
+ {
+ for (auto &Desc: TranslatedDescriptionsList(V))
+ {
+ pkgRecords::Parser &parser = records.Lookup(Desc.FileList());
+ PkgDescriptions.push_back(parser.LongDesc());
+ }
+ }
+
+ bool all_found = true;
+
+ char const * const PkgName = P.Name();
+ std::vector<bool> SkipDescription(PkgDescriptions.size(), false);
+ for (std::vector<regex_t>::const_iterator pattern = Patterns.begin();
+ pattern != Patterns.end(); ++pattern)
+ {
+ if (regexec(&(*pattern), PkgName, 0, 0, 0) == 0)
+ continue;
+ else if (not NamesOnly)
+ {
+ bool found = false;
+
+ for (std::vector<std::string>::size_type i = 0; i < PkgDescriptions.size(); ++i)
+ {
+ if (not SkipDescription[i])
+ {
+ if (regexec(&(*pattern), PkgDescriptions[i].c_str(), 0, 0, 0) == 0)
+ found = true;
+ else
+ SkipDescription[i] = true;
+ }
+ }
+
+ if (found)
+ continue;
+ }
+
+ // search patterns are AND, so one failing fails all
+ all_found = false;
+ break;
+ }
+
+ if (all_found == true)
+ {
+ PkgsDone[P->ID] = true;
+ std::stringstream outs;
+ ListSingleVersion(CacheFile, records, V, outs, format);
+ output_map.insert(std::make_pair<std::string, std::string>(
+ PkgName, outs.str()));
+ }
+ }
+ APT_FREE_PATTERNS();
+ progress.Done();
+
+ // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
+ // output the sorted map
+ std::map<std::string, std::string>::const_iterator K;
+ for (K = output_map.begin(); K != output_map.end(); ++K)
+ std::cout << (*K).second << std::endl;
+
+ if (output_map.empty())
+ RunJsonHook("AptCli::Hooks::Search", "org.debian.apt.hooks.search.fail", CmdL.FileList, CacheFile);
+ else
+ RunJsonHook("AptCli::Hooks::Search", "org.debian.apt.hooks.search.post", CmdL.FileList, CacheFile);
+ return true;
+}
+ /*}}}*/
+// LocalitySort - Sort a version list by package file locality /*{{{*/
+static int LocalityCompare(const void * const a, const void * const b)
+{
+ pkgCache::VerFile const * const A = *static_cast<pkgCache::VerFile const * const *>(a);
+ pkgCache::VerFile const * const B = *static_cast<pkgCache::VerFile const * const *>(b);
+
+ if (A == 0 && B == 0)
+ return 0;
+ if (A == 0)
+ return 1;
+ if (B == 0)
+ return -1;
+
+ if (A->File == B->File)
+ return A->Offset - B->Offset;
+ return A->File - B->File;
+}
+void LocalitySort(pkgCache::VerFile ** const begin, unsigned long long const Count,size_t const Size)
+{
+ qsort(begin,Count,Size,LocalityCompare);
+}
+static void LocalitySort(pkgCache::DescFile ** const begin, unsigned long long const Count,size_t const Size)
+{
+ qsort(begin,Count,Size,LocalityCompare);
+}
+ /*}}}*/
+// Search - Perform a search /*{{{*/
+// ---------------------------------------------------------------------
+/* This searches the package names and package descriptions for a pattern */
+struct ExDescFile
+{
+ pkgCache::DescFile *Df;
+ pkgCache::VerIterator V;
+ map_id_t ID;
+ ExDescFile() : Df(nullptr), ID(0) {}
+};
+static bool Search(CommandLine &CmdL)
+{
+ bool const ShowFull = _config->FindB("APT::Cache::ShowFull",false);
+ unsigned int const NumPatterns = CmdL.FileSize() -1;
+
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ pkgDepCache::Policy *Plcy = CacheFile.GetPolicy();
+ if (unlikely(Cache == NULL || Plcy == NULL))
+ return false;
+
+ // Make sure there is at least one argument
+ if (NumPatterns < 1)
+ return _error->Error(_("You must give at least one search pattern"));
+
+ // Compile the regex pattern
+ regex_t *Patterns = new regex_t[NumPatterns];
+ memset(Patterns,0,sizeof(*Patterns)*NumPatterns);
+ for (unsigned I = 0; I != NumPatterns; I++)
+ {
+ if (regcomp(&Patterns[I],CmdL.FileList[I+1],REG_EXTENDED | REG_ICASE |
+ REG_NOSUB) != 0)
+ {
+ for (; I != 0; I--)
+ regfree(&Patterns[I]);
+ return _error->Error("Regex compilation error");
+ }
+ }
+
+ if (_error->PendingError() == true)
+ {
+ for (unsigned I = 0; I != NumPatterns; I++)
+ regfree(&Patterns[I]);
+ return false;
+ }
+
+ size_t const descCount = Cache->HeaderP->GroupCount + 1;
+ ExDescFile *DFList = new ExDescFile[descCount];
+
+ bool *PatternMatch = new bool[descCount * NumPatterns];
+ memset(PatternMatch,false,sizeof(*PatternMatch) * descCount * NumPatterns);
+
+ // Map versions that we want to write out onto the VerList array.
+ bool const NamesOnly = _config->FindB("APT::Cache::NamesOnly",false);
+ for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G)
+ {
+ size_t const PatternOffset = G->ID * NumPatterns;
+ size_t unmatched = 0, matched = 0;
+ for (unsigned I = 0; I < NumPatterns; ++I)
+ {
+ if (PatternMatch[PatternOffset + I] == true)
+ ++matched;
+ else if (regexec(&Patterns[I],G.Name(),0,0,0) == 0)
+ PatternMatch[PatternOffset + I] = true;
+ else
+ ++unmatched;
+ }
+
+ // already dealt with this package?
+ if (matched == NumPatterns)
+ continue;
+
+ // Doing names only, drop any that don't match..
+ if (NamesOnly == true && unmatched == NumPatterns)
+ continue;
+
+ // Find the proper version to use
+ pkgCache::PkgIterator P = G.FindPreferredPkg();
+ if (P.end() == true)
+ continue;
+ pkgCache::VerIterator V = Plcy->GetCandidateVer(P);
+ if (V.end() == false)
+ {
+ pkgCache::DescIterator const D = V.TranslatedDescription();
+ //FIXME: packages without a description can't be found
+ if (D.end() == true)
+ continue;
+ DFList[G->ID].Df = D.FileList();
+ DFList[G->ID].V = V;
+ DFList[G->ID].ID = G->ID;
+ }
+
+ if (unmatched == NumPatterns)
+ continue;
+
+ // Include all the packages that provide matching names too
+ for (pkgCache::PrvIterator Prv = P.ProvidesList() ; Prv.end() == false; ++Prv)
+ {
+ pkgCache::VerIterator V = Plcy->GetCandidateVer(Prv.OwnerPkg());
+ if (V.end() == true)
+ continue;
+
+ unsigned long id = Prv.OwnerPkg().Group()->ID;
+ pkgCache::DescIterator const D = V.TranslatedDescription();
+ //FIXME: packages without a description can't be found
+ if (D.end() == true)
+ continue;
+ DFList[id].Df = D.FileList();
+ DFList[id].V = V;
+ DFList[id].ID = id;
+
+ size_t const PrvPatternOffset = id * NumPatterns;
+ for (unsigned I = 0; I < NumPatterns; ++I)
+ PatternMatch[PrvPatternOffset + I] |= PatternMatch[PatternOffset + I];
+ }
+ }
+
+ LocalitySort(&DFList->Df, Cache->HeaderP->GroupCount, sizeof(*DFList));
+
+ // Create the text record parser
+ pkgRecords Recs(*Cache);
+ // Iterate over all the version records and check them
+ for (ExDescFile *J = DFList; J->Df != 0; ++J)
+ {
+ size_t const PatternOffset = J->ID * NumPatterns;
+ if (not NamesOnly)
+ {
+ std::vector<std::string> PkgDescriptions;
+ for (auto &Desc: TranslatedDescriptionsList(J->V))
+ {
+ pkgRecords::Parser &parser = Recs.Lookup(Desc.FileList());
+ PkgDescriptions.push_back(parser.LongDesc());
+ }
+
+ std::vector<bool> SkipDescription(PkgDescriptions.size(), false);
+ for (unsigned I = 0; I < NumPatterns; ++I)
+ {
+ if (PatternMatch[PatternOffset + I])
+ continue;
+ else
+ {
+ bool found = false;
+
+ for (std::vector<std::string>::size_type k = 0; k < PkgDescriptions.size(); ++k)
+ {
+ if (not SkipDescription[k])
+ {
+ if (regexec(&Patterns[I], PkgDescriptions[k].c_str(), 0, 0, 0) == 0)
+ {
+ found = true;
+ PatternMatch[PatternOffset + I] = true;
+ }
+ else
+ SkipDescription[k] = true;
+ }
+ }
+
+ if (not found)
+ break;
+ }
+ }
+ }
+
+ bool matchedAll = true;
+ for (unsigned I = 0; I < NumPatterns; ++I)
+ if (PatternMatch[PatternOffset + I] == false)
+ {
+ matchedAll = false;
+ break;
+ }
+
+ if (matchedAll == true)
+ {
+ if (ShowFull == true)
+ {
+ pkgCache::VerFileIterator Vf;
+ auto &Parser = LookupParser(Recs, J->V, Vf);
+ char const *Start, *Stop;
+ Parser.GetRec(Start, Stop);
+ size_t const Length = Stop - Start;
+ DisplayRecordV1(CacheFile, Recs, J->V, Vf, Start, Length, std::cout);
+ }
+ else
+ {
+ pkgRecords::Parser &P = Recs.Lookup(pkgCache::DescFileIterator(*Cache, J->Df));
+ printf("%s - %s\n", P.Name().c_str(), P.ShortDesc().c_str());
+ }
+ }
+ }
+
+ delete [] DFList;
+ delete [] PatternMatch;
+ for (unsigned I = 0; I != NumPatterns; I++)
+ regfree(&Patterns[I]);
+ delete [] Patterns;
+ if (ferror(stdout))
+ return _error->Error("Write to stdout failed");
+ return true;
+}
+ /*}}}*/
+bool DoSearch(CommandLine &CmdL) /*{{{*/
+{
+ int const ShowVersion = _config->FindI("APT::Cache::Search::Version", 1);
+ if (ShowVersion <= 1)
+ return Search(CmdL);
+ return FullTextSearch(CmdL);
+}
+
diff --git a/apt-private/private-search.h b/apt-private/private-search.h
new file mode 100644
index 0000000..518f297
--- /dev/null
+++ b/apt-private/private-search.h
@@ -0,0 +1,12 @@
+#ifndef APT_PRIVATE_SEARCH_H
+#define APT_PRIVATE_SEARCH_H
+
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+
+class CommandLine;
+
+APT_PUBLIC bool DoSearch(CommandLine &CmdL);
+APT_PUBLIC void LocalitySort(pkgCache::VerFile ** const begin, unsigned long long const Count,size_t const Size);
+
+#endif
diff --git a/apt-private/private-show.cc b/apt-private/private-show.cc
new file mode 100644
index 0000000..30b99b0
--- /dev/null
+++ b/apt-private/private-show.cc
@@ -0,0 +1,587 @@
+// Includes /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/indexfile.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile.h>
+
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-install.h>
+#include <apt-private/private-show.h>
+
+#include <ostream>
+#include <string>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+pkgRecords::Parser &LookupParser(pkgRecords &Recs, pkgCache::VerIterator const &V, pkgCache::VerFileIterator &Vf) /*{{{*/
+{
+ Vf = V.FileList();
+ for (; Vf.end() == false; ++Vf)
+ if ((Vf.File()->Flags & pkgCache::Flag::NotSource) == 0)
+ break;
+ if (Vf.end() == true)
+ Vf = V.FileList();
+ return Recs.Lookup(Vf);
+}
+ /*}}}*/
+static APT_PURE char const *skipDescription(char const *DescP, size_t const Length, bool fields) /*{{{*/
+{
+ auto const backup = DescP;
+ char const * const TagName = "\nDescription";
+ size_t const TagLen = strlen(TagName);
+ while ((DescP = static_cast<char const *>(memchr(DescP, '\n', Length - (DescP - backup)))) != nullptr)
+ {
+ if (DescP[1] == ' ')
+ DescP += 2;
+ else if (fields && strncmp((char *)DescP, TagName, TagLen) == 0)
+ DescP += TagLen;
+ else
+ break;
+ }
+ if (DescP != NULL)
+ ++DescP;
+ return DescP;
+}
+ /*}}}*/
+static APT_PURE char const *findDescriptionField(char const *DescP, size_t const Length) /*{{{*/
+{
+ auto const backup = DescP;
+ char const * const TagName = "\nDescription";
+ size_t const TagLen = strlen(TagName);
+ while ((DescP = static_cast<char const *>(memchr(DescP, '\n', Length - (DescP - backup)))) != nullptr)
+ {
+ if (strncmp(DescP, TagName, TagLen) == 0)
+ break;
+ else
+ ++DescP;
+ }
+ if (DescP != nullptr)
+ ++DescP;
+ return DescP;
+}
+ /*}}}*/
+static APT_PURE char const *skipColonSpaces(char const *Buffer, size_t const Length) /*{{{*/
+{
+ // skipping withspace before and after the field-value separating colon
+ char const *const Start = Buffer;
+ for (; isspace(*Buffer) != 0 && Length - (Buffer - Start) > 0; ++Buffer)
+ ;
+ if (*Buffer != ':')
+ return nullptr;
+ ++Buffer;
+ for (; isspace(*Buffer) != 0 && Length - (Buffer - Start) > 0; ++Buffer)
+ ;
+ if (Length < static_cast<size_t>(Buffer - Start))
+ return nullptr;
+ return Buffer;
+}
+ /*}}}*/
+
+bool DisplayRecordV1(pkgCacheFile &, pkgRecords &Recs, /*{{{*/
+ pkgCache::VerIterator const &V, pkgCache::VerFileIterator const &Vf,
+ char const *Buffer, size_t Length, std::ostream &out)
+{
+ if (unlikely(Length < 4))
+ return false;
+
+ auto const Desc = V.TranslatedDescription();
+ if (Desc.end())
+ {
+ /* This handles the unusual case that we have no description whatsoever.
+ The slightly more common case of only having a short-description embedded
+ in the record could be handled here, but apt supports also having multiple
+ descriptions embedded in the record, so we deal with that case later */
+ if (FileFd::Write(STDOUT_FILENO, Buffer, Length) == false)
+ return false;
+ if (strncmp((Buffer + Length - 4), "\r\n\r\n", 4) != 0 &&
+ strncmp((Buffer + Length - 2), "\n\n", 2) != 0)
+ out << std::endl;
+ return true;
+ }
+
+ // Get a pointer to start of Description field
+ char const *DescP = findDescriptionField(Buffer, Length);
+ if (DescP == nullptr)
+ DescP = Buffer + Length;
+
+ // Write all but Description
+ size_t const untilDesc = DescP - Buffer;
+ if (untilDesc != 0 && FileFd::Write(STDOUT_FILENO, Buffer, untilDesc) == false)
+ return false;
+
+ // Show the right description
+ char desctag[50];
+ auto const langcode = Desc.LanguageCode();
+ if (strcmp(langcode, "") == 0)
+ strcpy(desctag, "\nDescription");
+ else
+ snprintf(desctag, sizeof(desctag), "\nDescription-%s", langcode);
+
+ out << desctag + 1 << ": " << std::flush;
+ auto const Df = Desc.FileList();
+ if (Df.end() == false)
+ {
+ if (Desc.FileList()->File == Vf->File)
+ {
+ /* If we have the file already open look in the buffer for the
+ description we want to display. Note that this might not be the
+ only one we can encounter in this record */
+ char const *Start = DescP;
+ do
+ {
+ if (strncmp(Start, desctag + 1, strlen(desctag) - 1) != 0)
+ continue;
+ Start += strlen(desctag) - 1;
+ Start = skipColonSpaces(Start, Length - (Start - Buffer));
+ if (Start == nullptr)
+ continue;
+ char const *End = skipDescription(Start, Length - (Start - Buffer), false);
+ if (likely(End != nullptr))
+ FileFd::Write(STDOUT_FILENO, Start, End - (Start + 1));
+ break;
+ } while ((Start = findDescriptionField(Start, Length - (Start - Buffer))) != nullptr);
+ }
+ else
+ {
+ pkgRecords::Parser &P = Recs.Lookup(Df);
+ out << P.LongDesc();
+ }
+ }
+
+ out << std::endl << "Description-md5: " << Desc.md5() << std::endl;
+
+ // Find the first field after the description (if there is any)
+ DescP = skipDescription(DescP, Length - (DescP - Buffer), true);
+
+ // write the rest of the buffer, but skip mixed in Descriptions* fields
+ while (DescP != nullptr)
+ {
+ char const *const Start = DescP;
+ char const *End = findDescriptionField(DescP, Length - (DescP - Buffer));
+ if (End == nullptr)
+ {
+ DescP = nullptr;
+ End = Buffer + Length - 1;
+ size_t endings = 0;
+ while (*End == '\n')
+ {
+ --End;
+ if (*End == '\r')
+ --End;
+ ++endings;
+ }
+ if (endings >= 1)
+ {
+ ++End;
+ if (*End == '\r')
+ ++End;
+ }
+ ++End;
+ }
+ else
+ DescP = skipDescription(End + strlen("Description"), Length - (End - Buffer), true);
+
+ size_t const length = End - Start;
+ if (length != 0 && FileFd::Write(STDOUT_FILENO, Start, length) == false)
+ return false;
+ }
+ // write a final newline after the last field
+ out << std::endl;
+
+ return true;
+}
+ /*}}}*/
+static bool DisplayRecordV2(pkgCacheFile &CacheFile, pkgRecords &Recs, /*{{{*/
+ pkgCache::VerIterator const &V, pkgCache::VerFileIterator const &Vf,
+ char const *Buffer, size_t const Length, std::ostream &out)
+{
+ // Check and load the package list file
+ pkgCache::PkgFileIterator I = Vf.File();
+
+ // find matching sources.list metaindex
+ pkgSourceList *SrcList = CacheFile.GetSourceList();
+ pkgIndexFile *Index;
+ if (SrcList->FindIndex(I, Index) == false &&
+ _system->FindIndex(I, Index) == false)
+ return _error->Error("Can not find indexfile for Package %s (%s)",
+ V.ParentPkg().Name(), V.VerStr());
+ std::string source_index_file = Index->Describe(true);
+
+ // Read the record
+ pkgTagSection Tags;
+ if (Tags.Scan(Buffer, Length, true) == false)
+ return _error->Error("Internal Error, Unable to parse a package record");
+
+ // make size nice
+ std::string installed_size;
+ if (Tags.FindULL("Installed-Size") > 0)
+ strprintf(installed_size, "%sB", SizeToStr(Tags.FindULL("Installed-Size") * 1024).c_str());
+ else
+ installed_size = _("unknown");
+ std::string package_size;
+ if (Tags.FindULL("Size") > 0)
+ strprintf(package_size, "%sB", SizeToStr(Tags.FindULL("Size")).c_str());
+ else
+ package_size = _("unknown");
+
+ const char *manual_installed = nullptr;
+ if (V.ParentPkg().CurrentVer() == V)
+ {
+ pkgDepCache *depCache = CacheFile.GetDepCache();
+ if (unlikely(depCache == nullptr))
+ return false;
+ pkgDepCache::StateCache &state = (*depCache)[V.ParentPkg()];
+ manual_installed = !(state.Flags & pkgCache::Flag::Auto) ? "yes" : "no";
+ }
+
+ // FIXME: add verbose that does not do the removal of the tags?
+ std::vector<pkgTagSection::Tag> RW;
+ // delete, apt-cache show has this info and most users do not care
+ RW.push_back(pkgTagSection::Tag::Remove("MD5sum"));
+ RW.push_back(pkgTagSection::Tag::Remove("SHA1"));
+ RW.push_back(pkgTagSection::Tag::Remove("SHA256"));
+ RW.push_back(pkgTagSection::Tag::Remove("SHA512"));
+ RW.push_back(pkgTagSection::Tag::Remove("Filename"));
+ RW.push_back(pkgTagSection::Tag::Remove("Multi-Arch"));
+ RW.push_back(pkgTagSection::Tag::Remove("Architecture"));
+ RW.push_back(pkgTagSection::Tag::Remove("Conffiles"));
+ // we use the translated description
+ RW.push_back(pkgTagSection::Tag::Remove("Description"));
+ RW.push_back(pkgTagSection::Tag::Remove("Description-md5"));
+ // improve
+ RW.push_back(pkgTagSection::Tag::Rewrite("Package", V.ParentPkg().FullName(true)));
+ RW.push_back(pkgTagSection::Tag::Rewrite("Installed-Size", installed_size));
+ RW.push_back(pkgTagSection::Tag::Remove("Size"));
+ RW.push_back(pkgTagSection::Tag::Rewrite("Download-Size", package_size));
+ // add
+ if (manual_installed != nullptr)
+ RW.push_back(pkgTagSection::Tag::Rewrite("APT-Manual-Installed", manual_installed));
+ RW.push_back(pkgTagSection::Tag::Rewrite("APT-Sources", source_index_file));
+
+ FileFd stdoutfd;
+ if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false) == false ||
+ Tags.Write(stdoutfd, TFRewritePackageOrder, RW) == false || stdoutfd.Close() == false)
+ return _error->Error("Internal Error, Unable to parse a package record");
+
+ // write the description
+ // FIXME: show (optionally) all available translations(?)
+ pkgCache::DescIterator Desc = V.TranslatedDescription();
+ if (Desc.end() == false)
+ {
+ pkgRecords::Parser &P = Recs.Lookup(Desc.FileList());
+ out << "Description: " << P.LongDesc();
+ }
+
+ // write a final newline (after the description)
+ out << std::endl << std::endl;
+
+ return true;
+}
+ /*}}}*/
+bool ShowPackage(CommandLine &CmdL) /*{{{*/
+{
+ pkgCacheFile CacheFile;
+ auto VolatileCmdL = GetAllPackagesAsPseudo(CacheFile.GetSourceList(), CmdL, AddVolatileBinaryFile, "");
+
+ if (unlikely(CacheFile.GetPkgCache() == nullptr))
+ return false;
+ CacheSetHelperVirtuals helper(true, GlobalError::NOTICE);
+ APT::CacheSetHelper::VerSelector const select = _config->FindB("APT::Cache::AllVersions", true) ?
+ APT::CacheSetHelper::ALL : APT::CacheSetHelper::CANDIDATE;
+ if (select == APT::CacheSetHelper::CANDIDATE && CacheFile.GetDepCache() == nullptr)
+ return false;
+
+ APT::VersionList verset;
+ size_t normalPackages = 0;
+ for (auto const &I: VolatileCmdL)
+ {
+ if (I.index == -1)
+ {
+ APT::VersionContainerInterface::FromString(&verset, CacheFile, I.name, select, helper);
+ ++normalPackages;
+ }
+ else
+ {
+ if (select != APT::CacheSetHelper::CANDIDATE && unlikely(CacheFile.GetDepCache() == nullptr))
+ return false;
+ pkgCache::PkgIterator const P = CacheFile->FindPkg(I.name);
+ if (unlikely(P.end()))
+ continue;
+
+ // Set any version providing the .deb as the candidate.
+ for (auto Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
+ {
+ if (I.release.empty())
+ CacheFile->SetCandidateVersion(Prv.OwnerVer());
+ else
+ CacheFile->SetCandidateRelease(Prv.OwnerVer(), I.release);
+
+ // via cacheset to have our usual handling
+ APT::VersionContainerInterface::FromPackage(&verset, CacheFile, Prv.OwnerPkg(), APT::CacheSetHelper::CANDIDATE, helper);
+ }
+ }
+ }
+
+ int const ShowVersion = _config->FindI("APT::Cache::Show::Version", 1);
+ pkgRecords Recs(CacheFile);
+ for (APT::VersionList::const_iterator Ver = verset.begin(); Ver != verset.end(); ++Ver)
+ {
+ pkgCache::VerFileIterator Vf;
+ auto &Parser = LookupParser(Recs, Ver, Vf);
+ char const *Start, *Stop;
+ Parser.GetRec(Start, Stop);
+ size_t const Length = Stop - Start;
+
+ if (ShowVersion <= 1)
+ {
+ if (DisplayRecordV1(CacheFile, Recs, Ver, Vf, Start, Length, std::cout) == false)
+ return false;
+ }
+ else if (DisplayRecordV2(CacheFile, Recs, Ver, Vf, Start, Length + 1, c1out) == false)
+ return false;
+ }
+
+ if (select == APT::CacheSetHelper::CANDIDATE && normalPackages != 0)
+ {
+ APT::VersionList verset_all;
+ for (auto const &I: VolatileCmdL)
+ {
+ if (I.index == -1)
+ APT::VersionContainerInterface::FromString(&verset_all, CacheFile, I.name, APT::CacheSetHelper::ALL, helper);
+ else
+ {
+ pkgCache::PkgIterator const P = CacheFile->FindPkg(I.name);
+ if (unlikely(P.end()))
+ continue;
+
+ // Set any version providing the .deb as the candidate.
+ for (auto Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
+ {
+ if (I.release.empty())
+ CacheFile->SetCandidateVersion(Prv.OwnerVer());
+ else
+ CacheFile->SetCandidateRelease(Prv.OwnerVer(), I.release);
+
+ // via cacheset to have our usual virtual handling
+ APT::VersionContainerInterface::FromPackage(&verset_all, CacheFile, Prv.OwnerPkg(), APT::CacheSetHelper::CANDIDATE, helper);
+ }
+ }
+ }
+
+ int const records = verset_all.size() - verset.size();
+ if (records > 0)
+ _error->Notice(P_("There is %i additional record. Please use the '-a' switch to see it", "There are %i additional records. Please use the '-a' switch to see them.", records), records);
+ }
+
+ if (_config->FindB("APT::Cache::ShowVirtuals", false) == true)
+ for (APT::PackageSet::const_iterator Pkg = helper.virtualPkgs.begin();
+ Pkg != helper.virtualPkgs.end(); ++Pkg)
+ {
+ c1out << "Package: " << Pkg.FullName(true) << std::endl;
+ c1out << "State: " << _("not a real package (virtual)") << std::endl;
+ // FIXME: show providers, see private-cacheset.h
+ // CacheSetHelperAPTGet::showVirtualPackageErrors()
+ }
+
+ if (verset.empty() == true)
+ {
+ if (helper.virtualPkgs.empty() == true)
+ return _error->Error(_("No packages found"));
+ else
+ _error->Notice(_("No packages found"));
+ }
+
+ return true;
+}
+ /*}}}*/
+static std::string Sha1FromString(std::string const &input) /*{{{*/
+{
+ // XXX: move to hashes.h: HashString::FromString() ?
+ Hashes sha1(Hashes::SHA1SUM);
+ sha1.Add(input.c_str(), input.length());
+ return sha1.GetHashString(Hashes::SHA1SUM).HashValue();
+}
+ /*}}}*/
+bool ShowSrcPackage(CommandLine &CmdL) /*{{{*/
+{
+ pkgCacheFile CacheFile;
+ pkgSourceList *List = CacheFile.GetSourceList();
+ if (unlikely(List == NULL))
+ return false;
+
+ // Create the text record parsers
+ pkgSrcRecords SrcRecs(*List);
+ if (_error->PendingError() == true)
+ return false;
+
+ bool found = false;
+ // avoid showing identical records
+ std::set<std::string> seen;
+ for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+ {
+ SrcRecs.Restart();
+
+ pkgSrcRecords::Parser *Parse;
+ bool found_this = false;
+ while ((Parse = SrcRecs.Find(*I,false)) != 0) {
+ // SrcRecs.Find() will find both binary and source names
+ if (_config->FindB("APT::Cache::Only-Source", false) == true)
+ if (Parse->Package() != *I)
+ continue;
+ std::string sha1str = Sha1FromString(Parse->AsStr());
+ if (std::find(seen.begin(), seen.end(), sha1str) == seen.end())
+ {
+ std::cout << Parse->AsStr() << std::endl;;
+ found = true;
+ found_this = true;
+ seen.insert(sha1str);
+ }
+ }
+ if (found_this == false) {
+ _error->Warning(_("Unable to locate package %s"),*I);
+ continue;
+ }
+ }
+ if (found == false)
+ _error->Notice(_("No packages found"));
+ return true;
+}
+ /*}}}*/
+// Policy - Show the results of the preferences file /*{{{*/
+bool Policy(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ pkgSourceList const * const SrcList = CacheFile.GetSourceList();
+ if (unlikely(SrcList == nullptr))
+ return false;
+ pkgCache * const Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == nullptr))
+ return false;
+ pkgPolicy * const Plcy = CacheFile.GetPolicy();
+ if (unlikely(Plcy == nullptr))
+ return false;
+
+ // Print out all of the package files
+ if (CmdL.FileList[1] == 0)
+ {
+ std::cout << _("Package files:") << std::endl;
+ for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F.end() == false; ++F)
+ {
+ if (F.Flagged(pkgCache::Flag::NoPackages))
+ continue;
+ // Locate the associated index files so we can derive a description
+ pkgIndexFile *Indx;
+ if (SrcList->FindIndex(F,Indx) == false &&
+ _system->FindIndex(F,Indx) == false)
+ return _error->Error(_("Cache is out of sync, can't x-ref a package file"));
+
+ printf("%4i %s\n",
+ Plcy->GetPriority(F),Indx->Describe(true).c_str());
+
+ // Print the reference information for the package
+ std::string Str = F.RelStr();
+ if (Str.empty() == false)
+ printf(" release %s\n",F.RelStr().c_str());
+ if (F.Site() != 0 && F.Site()[0] != 0)
+ printf(" origin %s\n",F.Site());
+ }
+
+ // Show any packages have explicit pins
+ std::cout << _("Pinned packages:") << std::endl;
+ pkgCache::PkgIterator I = Cache->PkgBegin();
+ for (;I.end() != true; ++I)
+ {
+ for (pkgCache::VerIterator V = I.VersionList(); !V.end(); ++V) {
+ auto Prio = Plcy->GetPriority(V, false);
+ if (Prio == 0)
+ continue;
+
+ std::cout << " ";
+ // Print the package name and the version we are forcing to
+ ioprintf(std::cout, _("%s -> %s with priority %d\n"), I.FullName(true).c_str(), V.VerStr(), Prio);
+ }
+ }
+ return true;
+ }
+
+ char const * const msgInstalled = _(" Installed: ");
+ char const * const msgCandidate = _(" Candidate: ");
+ short const InstalledLessCandidate =
+ mbstowcs(NULL, msgInstalled, 0) - mbstowcs(NULL, msgCandidate, 0);
+ short const deepInstalled =
+ (InstalledLessCandidate < 0 ? (InstalledLessCandidate*-1) : 0) - 1;
+ short const deepCandidate =
+ (InstalledLessCandidate > 0 ? (InstalledLessCandidate) : 0) - 1;
+
+ // Print out detailed information for each package
+ APT::CacheSetHelper helper(true, GlobalError::NOTICE);
+ APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1, helper);
+ for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
+ {
+ std::cout << Pkg.FullName(true) << ":" << std::endl;
+
+ // Installed version
+ std::cout << msgInstalled << OutputInDepth(deepInstalled, " ");
+ if (Pkg->CurrentVer == 0)
+ std::cout << _("(none)") << std::endl;
+ else
+ std::cout << Pkg.CurrentVer().VerStr() << std::endl;
+
+ // Candidate Version
+ std::cout << msgCandidate << OutputInDepth(deepCandidate, " ");
+ pkgCache::VerIterator V = Plcy->GetCandidateVer(Pkg);
+ if (V.end() == true)
+ std::cout << _("(none)") << std::endl;
+ else
+ std::cout << V.VerStr() << std::endl;
+
+ // Show the priority tables
+ std::cout << _(" Version table:") << std::endl;
+ for (V = Pkg.VersionList(); V.end() == false; ++V)
+ {
+ if (Pkg.CurrentVer() == V)
+ std::cout << " *** " << V.VerStr();
+ else
+ std::cout << " " << V.VerStr();
+
+ std::cout << " " << Plcy->GetPriority(V);
+
+ if (V.PhasedUpdatePercentage() != 100)
+ std::cout << " "
+ << "(" << _("phased") << " " << V.PhasedUpdatePercentage() << "%)";
+
+ std::cout << std::endl;
+ for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF)
+ {
+ // Locate the associated index files so we can derive a description
+ pkgIndexFile *Indx;
+ if (SrcList->FindIndex(VF.File(),Indx) == false &&
+ _system->FindIndex(VF.File(),Indx) == false)
+ return _error->Error(_("Cache is out of sync, can't x-ref a package file"));
+ printf(" %4i %s\n",Plcy->GetPriority(VF.File()),
+ Indx->Describe(true).c_str());
+ }
+ }
+ }
+ return true;
+}
+ /*}}}*/
diff --git a/apt-private/private-show.h b/apt-private/private-show.h
new file mode 100644
index 0000000..9e5fa99
--- /dev/null
+++ b/apt-private/private-show.h
@@ -0,0 +1,22 @@
+#ifndef APT_PRIVATE_SHOW_H
+#define APT_PRIVATE_SHOW_H
+
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+
+#include <iostream>
+
+class CommandLine;
+class pkgCacheFile;
+
+APT_PUBLIC bool ShowPackage(CommandLine &CmdL);
+APT_PUBLIC bool ShowSrcPackage(CommandLine &CmdL);
+APT_PUBLIC bool Policy(CommandLine &CmdL);
+
+pkgRecords::Parser &LookupParser(pkgRecords &Recs, pkgCache::VerIterator const &V, pkgCache::VerFileIterator &Vf);
+bool DisplayRecordV1(pkgCacheFile &CacheFile, pkgRecords &Recs,
+ pkgCache::VerIterator const &V, pkgCache::VerFileIterator const &Vf,
+ char const *Buffer, size_t const Length, std::ostream &out);
+
+#endif
diff --git a/apt-private/private-source.cc b/apt-private/private-source.cc
new file mode 100644
index 0000000..9b47ce3
--- /dev/null
+++ b/apt-private/private-source.cc
@@ -0,0 +1,891 @@
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/indexfile.h>
+#include <apt-pkg/metaindex.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/srcrecords.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/version.h>
+
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-install.h>
+#include <apt-private/private-source.h>
+
+#include <apt-pkg/debindexfile.h>
+#include <apt-pkg/deblistparser.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// GetReleaseFileForSourceRecord - Return Suite for the given srcrecord /*{{{*/
+static pkgCache::RlsFileIterator GetReleaseFileForSourceRecord(CacheFile &CacheFile,
+ pkgSourceList const * const SrcList, pkgSrcRecords::Parser const * const Parse)
+{
+ // try to find release
+ const pkgIndexFile& CurrentIndexFile = Parse->Index();
+
+ for (pkgSourceList::const_iterator S = SrcList->begin();
+ S != SrcList->end(); ++S)
+ {
+ std::vector<pkgIndexFile *> *Indexes = (*S)->GetIndexFiles();
+ for (std::vector<pkgIndexFile *>::const_iterator IF = Indexes->begin();
+ IF != Indexes->end(); ++IF)
+ {
+ if (&CurrentIndexFile == (*IF))
+ return (*S)->FindInCache(CacheFile, false);
+ }
+ }
+ return pkgCache::RlsFileIterator(CacheFile);
+}
+ /*}}}*/
+// FindSrc - Find a source record /*{{{*/
+static pkgSrcRecords::Parser *FindSrc(const char *Name,
+ pkgSrcRecords &SrcRecs,std::string &Src,
+ CacheFile &Cache)
+{
+ std::string VerTag, UserRequestedVerTag;
+ std::string ArchTag = "";
+ std::string RelTag = _config->Find("APT::Default-Release");
+ std::string TmpSrc = Name;
+
+ // extract release
+ size_t found = TmpSrc.find_last_of("/");
+ if (found != std::string::npos)
+ {
+ RelTag = TmpSrc.substr(found+1);
+ TmpSrc = TmpSrc.substr(0,found);
+ }
+ // extract the version
+ found = TmpSrc.find_last_of("=");
+ if (found != std::string::npos)
+ {
+ VerTag = UserRequestedVerTag = TmpSrc.substr(found+1);
+ TmpSrc = TmpSrc.substr(0,found);
+ }
+ // extract arch
+ found = TmpSrc.find_last_of(":");
+ if (found != std::string::npos)
+ {
+ ArchTag = TmpSrc.substr(found+1);
+ TmpSrc = TmpSrc.substr(0,found);
+ }
+
+ /* Lookup the version of the package we would install if we were to
+ install a version and determine the source package name, then look
+ in the archive for a source package of the same name. */
+ bool MatchSrcOnly = _config->FindB("APT::Get::Only-Source");
+ pkgCache::PkgIterator Pkg;
+ if (ArchTag != "")
+ Pkg = Cache.GetPkgCache()->FindPkg(TmpSrc, ArchTag);
+ else
+ Pkg = Cache.GetPkgCache()->FindPkg(TmpSrc);
+
+ // if we can't find a package but the user qualified with a arch,
+ // error out here
+ if (Pkg.end() && ArchTag != "")
+ {
+ Src = Name;
+ _error->Error(_("Can not find a package for architecture '%s'"),
+ ArchTag.c_str());
+ return 0;
+ }
+
+ if (MatchSrcOnly == false && Pkg.end() == false)
+ {
+ if(VerTag != "" || RelTag != "" || ArchTag != "")
+ {
+ bool fuzzy = false;
+ // we have a default release, try to locate the pkg. we do it like
+ // this because GetCandidateVer() will not "downgrade", that means
+ // "apt-get source -t stable apt" won't work on a unstable system
+ for (pkgCache::VerIterator Ver = Pkg.VersionList();; ++Ver)
+ {
+ // try first only exact matches, later fuzzy matches
+ if (Ver.end() == true)
+ {
+ if (fuzzy == true)
+ break;
+ fuzzy = true;
+ Ver = Pkg.VersionList();
+ // exit right away from the Pkg.VersionList() loop if we
+ // don't have any versions
+ if (Ver.end() == true)
+ break;
+ }
+
+ // ignore arches that are not for us
+ if (ArchTag != "" && Ver.Arch() != ArchTag)
+ continue;
+
+ // pick highest version for the arch unless the user wants
+ // something else
+ if (ArchTag != "" && VerTag == "" && RelTag == "")
+ if(Cache.GetPkgCache()->VS->CmpVersion(VerTag, Ver.VerStr()) < 0)
+ VerTag = Ver.VerStr();
+
+ // We match against a concrete version (or a part of this version)
+ if (VerTag.empty() == false &&
+ (fuzzy == true || Cache.GetPkgCache()->VS->CmpVersion(VerTag, Ver.VerStr()) != 0) && // exact match
+ (fuzzy == false || strncmp(VerTag.c_str(), Ver.VerStr(), VerTag.size()) != 0)) // fuzzy match
+ continue;
+
+ for (pkgCache::VerFileIterator VF = Ver.FileList();
+ VF.end() == false; ++VF)
+ {
+ /* If this is the status file, and the current version is not the
+ version in the status file (ie it is not installed, or somesuch)
+ then it is not a candidate for installation, ever. This weeds
+ out bogus entries that may be due to config-file states, or
+ other. */
+ if ((VF.File()->Flags & pkgCache::Flag::NotSource) ==
+ pkgCache::Flag::NotSource && Pkg.CurrentVer() != Ver)
+ continue;
+
+ // or we match against a release
+ if(VerTag.empty() == false ||
+ (VF.File().Archive() != 0 && VF.File().Archive() == RelTag) ||
+ (VF.File().Codename() != 0 && VF.File().Codename() == RelTag))
+ {
+ // the Version we have is possibly fuzzy or includes binUploads,
+ // so we use the Version of the SourcePkg (empty if same as package)
+ Src = Ver.SourcePkgName();
+ VerTag = Ver.SourceVerStr();
+ break;
+ }
+ }
+ if (Src.empty() == false)
+ break;
+ }
+ }
+
+ if (Src.empty() == true && ArchTag.empty() == false)
+ {
+ if (VerTag.empty() == false)
+ _error->Error(_("Can not find a package '%s' with version '%s'"),
+ Pkg.FullName().c_str(), VerTag.c_str());
+ if (RelTag.empty() == false)
+ _error->Error(_("Can not find a package '%s' with release '%s'"),
+ Pkg.FullName().c_str(), RelTag.c_str());
+ Src = Name;
+ return 0;
+ }
+
+
+ if (Src.empty() == true)
+ {
+ // if we don't have found a fitting package yet so we will
+ // choose a good candidate and proceed with that.
+ // Maybe we will find a source later on with the right VerTag
+ // or RelTag
+ if (Cache.BuildPolicy() == false)
+ return nullptr;
+ pkgPolicy * const Policy = Cache.GetPolicy();
+ pkgCache::VerIterator const Ver = Policy->GetCandidateVer(Pkg);
+ if (Ver.end() == false)
+ {
+ if (strcmp(Ver.SourcePkgName(),Ver.ParentPkg().Name()) != 0)
+ Src = Ver.SourcePkgName();
+ if (VerTag.empty() == true && strcmp(Ver.SourceVerStr(),Ver.VerStr()) != 0)
+ VerTag = Ver.SourceVerStr();
+ }
+ }
+ }
+
+ if (Src.empty() == true)
+ {
+ Src = TmpSrc;
+ }
+ else
+ {
+ /* if we have a source pkg name, make sure to only search
+ for srcpkg names, otherwise apt gets confused if there
+ is a binary package "pkg1" and a source package "pkg1"
+ with the same name but that comes from different packages */
+ MatchSrcOnly = true;
+ if (Src != TmpSrc)
+ {
+ ioprintf(c1out, _("Picking '%s' as source package instead of '%s'\n"), Src.c_str(), TmpSrc.c_str());
+ }
+ }
+
+ // The best hit
+ pkgSrcRecords::Parser *Last = 0;
+ unsigned long Offset = 0;
+ std::string Version;
+ pkgSourceList const * const SrcList = Cache.GetSourceList();
+
+ /* Iterate over all of the hits, which includes the resulting
+ binary packages in the search */
+ pkgSrcRecords::Parser *Parse;
+ while (true)
+ {
+ SrcRecs.Restart();
+ while ((Parse = SrcRecs.Find(Src.c_str(), MatchSrcOnly)) != 0)
+ {
+ const std::string Ver = Parse->Version();
+
+ // See if we need to look for a specific release tag
+ if (RelTag.empty() == false && UserRequestedVerTag.empty() == true)
+ {
+ pkgCache::RlsFileIterator const Rls = GetReleaseFileForSourceRecord(Cache, SrcList, Parse);
+ if (Rls.end() == false)
+ {
+ if ((Rls->Archive != 0 && RelTag != Rls.Archive()) &&
+ (Rls->Codename != 0 && RelTag != Rls.Codename()))
+ continue;
+ }
+ }
+
+ // Ignore all versions which doesn't fit
+ if (VerTag.empty() == false &&
+ Cache.GetPkgCache()->VS->CmpVersion(VerTag, Ver) != 0) // exact match
+ continue;
+
+ // Newer version or an exact match? Save the hit
+ if (Last == 0 || Cache.GetPkgCache()->VS->CmpVersion(Version,Ver) < 0) {
+ Last = Parse;
+ Offset = Parse->Offset();
+ Version = Ver;
+ }
+
+ // was the version check above an exact match?
+ // If so, we don't need to look further
+ if (VerTag.empty() == false && (VerTag == Ver))
+ break;
+ }
+ if (UserRequestedVerTag == "" && Version != "" && RelTag != "")
+ ioprintf(c1out, "Selected version '%s' (%s) for %s\n",
+ Version.c_str(), RelTag.c_str(), Src.c_str());
+
+ if (Last != 0 || VerTag.empty() == true)
+ break;
+ _error->Error(_("Can not find version '%s' of package '%s'"), VerTag.c_str(), TmpSrc.c_str());
+ return 0;
+ }
+
+ if (Last == 0 || Last->Jump(Offset) == false)
+ return 0;
+
+ return Last;
+}
+ /*}}}*/
+// DoSource - Fetch a source archive /*{{{*/
+// ---------------------------------------------------------------------
+/* Fetch source packages */
+struct DscFile
+{
+ std::string Package;
+ std::string Version;
+ std::string Dsc;
+};
+bool DoSource(CommandLine &CmdL)
+{
+ if (CmdL.FileSize() <= 1)
+ return _error->Error(_("Must specify at least one package to fetch source for"));
+
+ CacheFile Cache;
+ if (Cache.BuildCaches(false) == false)
+ return false;
+
+ // Create the text record parsers
+ pkgSourceList * const List = Cache.GetSourceList();
+ pkgSrcRecords SrcRecs(*List);
+ if (_error->PendingError() == true)
+ return false;
+
+ std::vector<DscFile> Dsc;
+ Dsc.reserve(CmdL.FileSize());
+
+ // insert all downloaded uris into this set to avoid downloading them
+ // twice
+ std::set<std::string> queued;
+
+ // Diff only mode only fetches .diff files
+ bool const diffOnly = _config->FindB("APT::Get::Diff-Only", false);
+ // Tar only mode only fetches .tar files
+ bool const tarOnly = _config->FindB("APT::Get::Tar-Only", false);
+ // Dsc only mode only fetches .dsc files
+ bool const dscOnly = _config->FindB("APT::Get::Dsc-Only", false);
+
+ // Load the requestd sources into the fetcher
+ aptAcquireWithTextStatus Fetcher;
+ std::vector<std::string> UntrustedList;
+ for (const char **cmdl = CmdL.FileList + 1; *cmdl != 0; ++cmdl)
+ {
+ std::string Src;
+ pkgSrcRecords::Parser *Last = FindSrc(*cmdl, SrcRecs, Src, Cache);
+ if (Last == 0) {
+ return _error->Error(_("Unable to find a source package for %s"),Src.c_str());
+ }
+
+ if (Last->Index().IsTrusted() == false)
+ UntrustedList.push_back(Src);
+
+ std::string srec = Last->AsStr();
+ std::string::size_type pos = srec.find("\nVcs-");
+ while (pos != std::string::npos)
+ {
+ pos += strlen("\nVcs-");
+ std::string vcs = srec.substr(pos,srec.find(":",pos)-pos);
+ if(vcs == "Browser")
+ {
+ pos = srec.find("\nVcs-", pos);
+ continue;
+ }
+ pos += vcs.length()+2;
+ std::string::size_type epos = srec.find("\n", pos);
+ std::string const uri = srec.substr(pos,epos-pos);
+ ioprintf(c1out, _("NOTICE: '%s' packaging is maintained in "
+ "the '%s' version control system at:\n"
+ "%s\n"),
+ Src.c_str(), vcs.c_str(), uri.c_str());
+ std::string vcscmd;
+ if (vcs == "Bzr")
+ vcscmd = "bzr branch " + uri;
+ else if (vcs == "Git")
+ vcscmd = "git clone " + uri;
+
+ if (vcscmd.empty() == false)
+ ioprintf(c1out,_("Please use:\n%s\n"
+ "to retrieve the latest (possibly unreleased) "
+ "updates to the package.\n"),
+ vcscmd.c_str());
+ break;
+ }
+
+ // Back track
+ std::vector<pkgSrcRecords::File> Lst;
+ if (Last->Files(Lst) == false) {
+ return false;
+ }
+
+ DscFile curDsc;
+ // Load them into the fetcher
+ for (std::vector<pkgSrcRecords::File>::const_iterator I = Lst.begin();
+ I != Lst.end(); ++I)
+ {
+ // Try to guess what sort of file it is we are getting.
+ if (I->Type == "dsc")
+ {
+ curDsc.Package = Last->Package();
+ curDsc.Version = Last->Version();
+ curDsc.Dsc = flNotDir(I->Path);
+ }
+
+ // Handle the only options so that multiple can be used at once
+ if (diffOnly == true || tarOnly == true || dscOnly == true)
+ {
+ if ((diffOnly == true && I->Type == "diff") ||
+ (tarOnly == true && I->Type == "tar") ||
+ (dscOnly == true && I->Type == "dsc"))
+ ; // Fine, we want this file downloaded
+ else
+ continue;
+ }
+
+ // don't download the same uri twice (should this be moved to
+ // the fetcher interface itself?)
+ if(queued.find(Last->Index().ArchiveURI(I->Path)) != queued.end())
+ continue;
+ queued.insert(Last->Index().ArchiveURI(I->Path));
+
+ // check if we have a file with that md5 sum already localy
+ std::string localFile = flNotDir(I->Path);
+ if (FileExists(localFile) == true)
+ if(I->Hashes.VerifyFile(localFile) == true)
+ {
+ ioprintf(c1out,_("Skipping already downloaded file '%s'\n"),
+ localFile.c_str());
+ continue;
+ }
+
+ // see if we have a hash (Acquire::ForceHash is the only way to have none)
+ if (I->Hashes.usable() == false && _config->FindB("APT::Get::AllowUnauthenticated",false) == false)
+ {
+ ioprintf(c1out, "Skipping download of file '%s' as requested hashsum is not available for authentication\n",
+ localFile.c_str());
+ curDsc.Dsc.clear();
+ continue;
+ }
+
+ new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path),
+ I->Hashes, I->FileSize, Last->Index().SourceInfo(*Last,*I), Src);
+ }
+ Dsc.push_back(std::move(curDsc));
+ }
+
+ // Display statistics
+ unsigned long long FetchBytes = Fetcher.FetchNeeded();
+ unsigned long long FetchPBytes = Fetcher.PartialPresent();
+ unsigned long long DebBytes = Fetcher.TotalNeeded();
+
+ if (CheckFreeSpaceBeforeDownload(".", (FetchBytes - FetchPBytes)) == false)
+ return false;
+
+ // Number of bytes
+ if (DebBytes != FetchBytes)
+ //TRANSLATOR: The required space between number and unit is already included
+ // in the replacement strings, so %sB will be correctly translate in e.g. 1,5 MB
+ ioprintf(c1out,_("Need to get %sB/%sB of source archives.\n"),
+ SizeToStr(FetchBytes).c_str(),SizeToStr(DebBytes).c_str());
+ else
+ //TRANSLATOR: The required space between number and unit is already included
+ // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB
+ ioprintf(c1out,_("Need to get %sB of source archives.\n"),
+ SizeToStr(DebBytes).c_str());
+
+ if (_config->FindB("APT::Get::Simulate",false) == true)
+ {
+ for (auto const &D: Dsc)
+ ioprintf(std::cout, _("Fetch source %s\n"), D.Package.c_str());
+ return true;
+ }
+
+ // Just print out the uris an exit if the --print-uris flag was used
+ if (_config->FindB("APT::Get::Print-URIs") == true)
+ {
+ pkgAcquire::UriIterator I = Fetcher.UriBegin();
+ for (; I != Fetcher.UriEnd(); ++I)
+ std::cout << '\'' << I->URI << "' " << flNotDir(I->Owner->DestFile) << ' ' <<
+ std::to_string(I->Owner->FileSize) << ' ' << I->Owner->HashSum() << std::endl;
+ return true;
+ }
+
+ // check authentication status of the source as well
+ if (UntrustedList.empty() == false && AuthPrompt(UntrustedList, false) == false)
+ return false;
+
+ // Run it
+ bool Failed = false;
+ if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == true)
+ return _error->Error(_("Failed to fetch some archives."));
+
+ if (diffOnly || tarOnly || dscOnly || _config->FindB("APT::Get::Download-only",false) == true)
+ {
+ c1out << _("Download complete and in download only mode") << std::endl;
+ return true;
+ }
+
+ bool const fixBroken = _config->FindB("APT::Get::Fix-Broken", false);
+ bool SaidCheckIfDpkgDev = false;
+ for (auto const &D: Dsc)
+ {
+ if (unlikely(D.Dsc.empty() == true))
+ continue;
+ std::string const Dir = D.Package + '-' + Cache.GetPkgCache()->VS->UpstreamVersion(D.Version.c_str());
+
+ // See if the package is already unpacked
+ struct stat Stat;
+ if (fixBroken == false && stat(Dir.c_str(),&Stat) == 0 &&
+ S_ISDIR(Stat.st_mode) != 0)
+ {
+ ioprintf(c0out ,_("Skipping unpack of already unpacked source in %s\n"),
+ Dir.c_str());
+ }
+ else
+ {
+ // Call dpkg-source
+ std::string const sourceopts = _config->Find("DPkg::Source-Options", "--no-check -x");
+ std::string S;
+ strprintf(S, "%s %s %s",
+ _config->Find("Dir::Bin::dpkg-source","dpkg-source").c_str(),
+ sourceopts.c_str(), D.Dsc.c_str());
+ if (system(S.c_str()) != 0)
+ {
+ _error->Error(_("Unpack command '%s' failed.\n"), S.c_str());
+ if (SaidCheckIfDpkgDev == false)
+ {
+ _error->Notice(_("Check if the 'dpkg-dev' package is installed.\n"));
+ SaidCheckIfDpkgDev = true;
+ }
+ continue;
+ }
+ }
+
+ // Try to compile it with dpkg-buildpackage
+ if (_config->FindB("APT::Get::Compile",false) == true)
+ {
+ std::string buildopts = _config->Find("APT::Get::Host-Architecture");
+ if (buildopts.empty() == false)
+ buildopts = "-a" + buildopts + " ";
+
+ // get all active build profiles
+ std::string const profiles = APT::Configuration::getBuildProfilesString();
+ if (profiles.empty() == false)
+ buildopts.append(" -P").append(profiles).append(" ");
+
+ buildopts.append(_config->Find("DPkg::Build-Options","-b -uc"));
+
+ // Call dpkg-buildpackage
+ std::string S;
+ strprintf(S, "cd %s && %s %s",
+ Dir.c_str(),
+ _config->Find("Dir::Bin::dpkg-buildpackage","dpkg-buildpackage").c_str(),
+ buildopts.c_str());
+
+ if (system(S.c_str()) != 0)
+ {
+ _error->Error(_("Build command '%s' failed.\n"), S.c_str());
+ continue;
+ }
+ }
+ }
+ return true;
+}
+ /*}}}*/
+// DoBuildDep - Install/removes packages to satisfy build dependencies /*{{{*/
+// ---------------------------------------------------------------------
+/* This function will look at the build depends list of the given source
+ package and install the necessary packages to make it true, or fail. */
+static std::vector<pkgSrcRecords::Parser::BuildDepRec> GetBuildDeps(pkgSrcRecords::Parser * const Last,
+ char const * const Src, bool const StripMultiArch, std::string const &hostArch)
+{
+ std::vector<pkgSrcRecords::Parser::BuildDepRec> BuildDeps;
+ // FIXME: Can't specify architecture to use for [wildcard] matching, so switch default arch temporary
+ if (hostArch.empty() == false)
+ {
+ std::string nativeArch = _config->Find("APT::Architecture");
+ _config->Set("APT::Architecture", hostArch);
+ bool Success = Last->BuildDepends(BuildDeps, _config->FindB("APT::Get::Arch-Only", false), StripMultiArch);
+ _config->Set("APT::Architecture", nativeArch);
+ if (Success == false)
+ {
+ _error->Error(_("Unable to get build-dependency information for %s"), Src);
+ return {};
+ }
+ }
+ else if (Last->BuildDepends(BuildDeps, _config->FindB("APT::Get::Arch-Only", false), StripMultiArch) == false)
+ {
+ _error->Error(_("Unable to get build-dependency information for %s"), Src);
+ return {};
+ }
+
+ if (BuildDeps.empty() == true)
+ ioprintf(c1out,_("%s has no build depends.\n"), Src);
+
+ return BuildDeps;
+}
+static void WriteBuildDependencyPackage(std::ostringstream &buildDepsPkgFile,
+ std::string const &PkgName, std::string const &Arch,
+ std::vector<pkgSrcRecords::Parser::BuildDepRec> const &Dependencies)
+{
+ buildDepsPkgFile << "Package: " << PkgName << "\n"
+ << "Architecture: " << Arch << "\n"
+ << "Version: 1\n";
+
+ bool const IndepOnly = _config->FindB("APT::Get::Indep-Only", false);
+ std::string depends, conflicts;
+ for (auto const &dep: Dependencies)
+ {
+ // ArchOnly is handled while parsing the dependencies on input
+ if (IndepOnly && (dep.Type == pkgSrcRecords::Parser::BuildDependArch ||
+ dep.Type == pkgSrcRecords::Parser::BuildConflictArch))
+ continue;
+ std::string * type;
+ if (dep.Type == pkgSrcRecords::Parser::BuildConflict ||
+ dep.Type == pkgSrcRecords::Parser::BuildConflictIndep ||
+ dep.Type == pkgSrcRecords::Parser::BuildConflictArch)
+ type = &conflicts;
+ else
+ type = &depends;
+
+ type->append(" ").append(dep.Package);
+ if (dep.Version.empty() == false)
+ type->append(" (").append(pkgCache::CompTypeDeb(dep.Op)).append(" ").append(dep.Version).append(")");
+ if ((dep.Op & pkgCache::Dep::Or) == pkgCache::Dep::Or)
+ {
+ type->append("\n |");
+ }
+ else
+ type->append(",\n");
+ }
+ if (depends.empty() == false)
+ buildDepsPkgFile << "Depends:\n" << depends;
+ if (conflicts.empty() == false)
+ buildDepsPkgFile << "Conflicts:\n" << conflicts;
+ buildDepsPkgFile << "\n";
+}
+bool DoBuildDep(CommandLine &CmdL)
+{
+ bool StripMultiArch;
+ std::string hostArch = _config->Find("APT::Get::Host-Architecture");
+ if (hostArch.empty() == false)
+ {
+ std::vector<std::string> archs = APT::Configuration::getArchitectures();
+ if (std::find(archs.begin(), archs.end(), hostArch) == archs.end())
+ return _error->Error(_("No architecture information available for %s. See apt.conf(5) APT::Architectures for setup"), hostArch.c_str());
+ StripMultiArch = false;
+ }
+ else
+ StripMultiArch = true;
+ auto const nativeArch = _config->Find("APT::Architecture");
+ std::string const pseudoArch = hostArch.empty() ? nativeArch : hostArch;
+
+ CacheFile Cache;
+ auto VolatileCmdL = GetPseudoPackages(Cache.GetSourceList(), CmdL, AddVolatileSourceFile, pseudoArch);
+ auto AreDoingSatisfy = strcasecmp(CmdL.FileList[0], "satisfy") == 0;
+
+ if (not AreDoingSatisfy)
+ _config->Set("APT::Install-Recommends", false);
+
+ if (CmdL.FileSize() <= 1 && VolatileCmdL.empty())
+ return _error->Error(_("Must specify at least one package to check builddeps for"));
+
+ std::ostringstream buildDepsPkgFile;
+ std::vector<PseudoPkg> pseudoPkgs;
+ // deal with the build essentials first
+ if (not AreDoingSatisfy)
+ {
+ std::vector<pkgSrcRecords::Parser::BuildDepRec> BuildDeps;
+ for (auto && opt: _config->FindVector("APT::Build-Essential"))
+ {
+ if (opt.empty())
+ continue;
+ pkgSrcRecords::Parser::BuildDepRec rec;
+ rec.Package = std::move(opt);
+ rec.Type = pkgSrcRecords::Parser::BuildDependIndep;
+ rec.Op = 0;
+ BuildDeps.push_back(rec);
+ }
+ std::string const pseudo = "builddeps:essentials";
+ WriteBuildDependencyPackage(buildDepsPkgFile, pseudo, nativeArch, BuildDeps);
+ pseudoPkgs.emplace_back(pseudo, nativeArch, "");
+ }
+
+ if (AreDoingSatisfy)
+ {
+ std::vector<pkgSrcRecords::Parser::BuildDepRec> BuildDeps;
+ for (unsigned i = 1; i < CmdL.FileSize(); i++)
+ {
+ const char *Start = CmdL.FileList[i];
+ const char *Stop = Start + strlen(Start);
+ auto Type = pkgSrcRecords::Parser::BuildDependIndep;
+
+ // Reject '>' and '<' as operators, as they have strange meanings.
+ bool insideVersionRestriction = false;
+ for (auto C = Start; C + 1 < Stop; C++)
+ {
+ if (*C == '(')
+ insideVersionRestriction = true;
+ else if (*C == ')')
+ insideVersionRestriction = false;
+ else if (insideVersionRestriction && (*C == '<' || *C == '>'))
+ {
+ if (C[1] != *C && C[1] != '=')
+ return _error->Error(_("Invalid operator '%c' at offset %d, did you mean '%c%c' or '%c='? - in: %s"), *C, (int)(C - Start), *C, *C, *C, Start);
+ C++;
+ }
+ }
+
+ if (APT::String::Startswith(Start, "Conflicts:"))
+ {
+ Type = pkgSrcRecords::Parser::BuildConflictIndep;
+ Start += strlen("Conflicts:");
+ }
+ while (1)
+ {
+ pkgSrcRecords::Parser::BuildDepRec rec;
+ Start = debListParser::ParseDepends(Start, Stop,
+ rec.Package, rec.Version, rec.Op, true, false, true, pseudoArch);
+
+ if (Start == 0)
+ return _error->Error("Problem parsing dependency: %s", CmdL.FileList[i]);
+ rec.Type = Type;
+
+ // We parsed a package that was ignored (wrong architecture restriction
+ // or something).
+ if (rec.Package.empty())
+ {
+ // If we are in an OR group, we need to set the "Or" flag of the
+ // previous entry to our value.
+ if (BuildDeps.empty() == false && (BuildDeps[BuildDeps.size() - 1].Op & pkgCache::Dep::Or) == pkgCache::Dep::Or)
+ {
+ BuildDeps[BuildDeps.size() - 1].Op &= ~pkgCache::Dep::Or;
+ BuildDeps[BuildDeps.size() - 1].Op |= (rec.Op & pkgCache::Dep::Or);
+ }
+ }
+ else
+ {
+ BuildDeps.emplace_back(std::move(rec));
+ }
+
+ if (Start == Stop)
+ break;
+ }
+ }
+ std::string const pseudo = "command line argument";
+ WriteBuildDependencyPackage(buildDepsPkgFile, pseudo, pseudoArch, BuildDeps);
+ pseudoPkgs.emplace_back(pseudo, pseudoArch, "");
+ }
+
+ // Read the source list
+ if (Cache.BuildSourceList() == false)
+ return false;
+ pkgSourceList *List = Cache.GetSourceList();
+
+ if (not AreDoingSatisfy)
+ {
+ auto const VolatileSources = List->GetVolatileFiles();
+ for (auto &&pkg : VolatileCmdL)
+ {
+ if (unlikely(pkg.index == -1))
+ {
+ _error->Error(_("Unable to find a source package for %s"), pkg.name.c_str());
+ continue;
+ }
+ if (DirectoryExists(pkg.name))
+ ioprintf(c1out, _("Note, using directory '%s' to get the build dependencies\n"), pkg.name.c_str());
+ else
+ ioprintf(c1out, _("Note, using file '%s' to get the build dependencies\n"), pkg.name.c_str());
+ std::unique_ptr<pkgSrcRecords::Parser> Last(VolatileSources[pkg.index]->CreateSrcParser());
+ if (Last == nullptr)
+ {
+ _error->Error(_("Unable to find a source package for %s"), pkg.name.c_str());
+ continue;
+ }
+
+ auto pseudo = std::string("builddeps:") + pkg.name;
+ WriteBuildDependencyPackage(buildDepsPkgFile, pseudo, pseudoArch,
+ GetBuildDeps(Last.get(), pkg.name.c_str(), StripMultiArch, hostArch));
+ pkg.name = std::move(pseudo);
+ pseudoPkgs.push_back(std::move(pkg));
+ }
+ VolatileCmdL.clear();
+ }
+
+ bool const WantLock = _config->FindB("APT::Get::Print-URIs", false) == false;
+ if (CmdL.FileList[1] != 0 && not AreDoingSatisfy)
+ {
+ if (Cache.BuildCaches(WantLock) == false)
+ return false;
+ // Create the text record parsers
+ pkgSrcRecords SrcRecs(*List);
+ if (_error->PendingError() == true)
+ return false;
+ for (const char **I = CmdL.FileList + 1; *I != 0; ++I)
+ {
+ std::string Src;
+ pkgSrcRecords::Parser * const Last = FindSrc(*I,SrcRecs,Src,Cache);
+ if (Last == nullptr)
+ return _error->Error(_("Unable to find a source package for %s"), *I);
+
+ std::string const pseudo = std::string("builddeps:") + Src;
+ WriteBuildDependencyPackage(buildDepsPkgFile, pseudo, pseudoArch,
+ GetBuildDeps(Last, Src.c_str(), StripMultiArch, hostArch));
+ std::string reltag = *I;
+ size_t found = reltag.find_last_of("/");
+ if (found == std::string::npos)
+ reltag.clear();
+ else
+ reltag.erase(0, found + 1);
+ pseudoPkgs.emplace_back(pseudo, pseudoArch, std::move(reltag));
+ }
+ }
+
+ Cache.AddIndexFile(new debStringPackageIndex(buildDepsPkgFile.str()));
+
+ if (Cache.Open(WantLock) == false)
+ return false;
+ pkgProblemResolver Fix(Cache.GetDepCache());
+
+ APT::PackageVector removeAgain;
+ {
+ pkgDepCache::ActionGroup group(Cache);
+ TryToInstall InstallAction(Cache, &Fix, false);
+ std::list<std::pair<pkgCache::VerIterator, std::string>> candSwitch;
+ for (auto const &pkg: pseudoPkgs)
+ {
+ pkgCache::PkgIterator const Pkg = Cache->FindPkg(pkg.name, pkg.arch);
+ if (Pkg.end())
+ continue;
+ if (pkg.release.empty())
+ Cache->SetCandidateVersion(Pkg.VersionList());
+ else
+ candSwitch.emplace_back(Pkg.VersionList(), pkg.release);
+ }
+ if (candSwitch.empty() == false)
+ InstallAction.propergateReleaseCandiateSwitching(candSwitch, c0out);
+ for (auto const &pkg: pseudoPkgs)
+ {
+ pkgCache::PkgIterator const Pkg = Cache->FindPkg(pkg.name, pkg.arch);
+ if (Pkg.end())
+ continue;
+ InstallAction(Cache[Pkg].CandidateVerIter(Cache));
+ removeAgain.push_back(Pkg);
+ }
+ InstallAction.doAutoInstall();
+
+ OpTextProgress Progress(*_config);
+ bool const resolver_fail = Fix.Resolve(true, &Progress);
+ if (resolver_fail == false && Cache->BrokenCount() == 0)
+ return false;
+ if (CheckNothingBroken(Cache) == false)
+ return false;
+ }
+ if (DoAutomaticRemove(Cache) == false)
+ return false;
+
+ {
+ pkgDepCache::ActionGroup group(Cache);
+ if (_config->FindB(AreDoingSatisfy ? "APT::Get::Satisfy-Automatic" : "APT::Get::Build-Dep-Automatic", false) == false)
+ {
+ for (auto const &pkg: removeAgain)
+ {
+ auto const instVer = Cache[pkg].InstVerIter(Cache);
+ if (unlikely(instVer.end() == true))
+ continue;
+ for (auto D = instVer.DependsList(); D.end() != true; ++D)
+ {
+ if (D->Type != pkgCache::Dep::Depends || D.IsMultiArchImplicit())
+ continue;
+ APT::VersionList verlist = APT::VersionList::FromDependency(Cache, D, APT::CacheSetHelper::CANDIDATE);
+ for (auto const &V : verlist)
+ {
+ auto const P = V.ParentPkg();
+ if (Cache[P].InstallVer != V)
+ continue;
+ Cache->MarkAuto(P, false);
+ }
+ }
+ }
+ }
+ for (auto const &pkg: removeAgain)
+ Cache->MarkDelete(pkg, false, 0, true);
+ }
+
+ pseudoPkgs.clear();
+ if (_error->PendingError() || InstallPackages(Cache, false, true) == false)
+ return _error->Error(_("Failed to process build dependencies"));
+ return true;
+}
+ /*}}}*/
diff --git a/apt-private/private-source.h b/apt-private/private-source.h
new file mode 100644
index 0000000..d00c53e
--- /dev/null
+++ b/apt-private/private-source.h
@@ -0,0 +1,11 @@
+#ifndef APT_PRIVATE_SOURCE_H
+#define APT_PRIVATE_SOURCE_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool DoSource(CommandLine &CmdL);
+APT_PUBLIC bool DoBuildDep(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-sources.cc b/apt-private/private-sources.cc
new file mode 100644
index 0000000..4cfb879
--- /dev/null
+++ b/apt-private/private-sources.cc
@@ -0,0 +1,105 @@
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-output.h>
+#include <apt-private/private-sources.h>
+#include <apt-private/private-utils.h>
+
+#include <iostream>
+#include <string>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+
+/* Interface discussion with donkult (for the future):
+ apt [add-{archive,release,component}|edit|change-release|disable]-sources
+ and be clever and work out stuff from the Release file
+*/
+
+// EditSource - EditSourcesList /*{{{*/
+class APT_HIDDEN ScopedGetLock {
+public:
+ int fd;
+ explicit ScopedGetLock(std::string const &filename) : fd(GetLock(filename)) {}
+ ~ScopedGetLock() { close(fd); }
+};
+bool EditSources(CommandLine &CmdL)
+{
+ std::string sourceslist;
+ if (CmdL.FileList[1] != NULL)
+ {
+ sourceslist = _config->FindDir("Dir::Etc::sourceparts") + CmdL.FileList[1];
+ if (!APT::String::Endswith(sourceslist, ".list"))
+ sourceslist += ".list";
+ } else {
+ sourceslist = _config->FindFile("Dir::Etc::sourcelist");
+ }
+ HashString before;
+ if (FileExists(sourceslist))
+ before.FromFile(sourceslist);
+ else
+ {
+ FileFd filefd;
+ if (filefd.Open(sourceslist, FileFd::Create | FileFd::WriteOnly, FileFd::None, 0644) == false)
+ return false;
+ }
+
+ ScopedGetLock lock(sourceslist);
+ if (lock.fd < 0)
+ return false;
+
+ bool res;
+ bool file_changed = false;
+ do {
+ if (EditFileInSensibleEditor(sourceslist) == false)
+ return false;
+ if (before.empty())
+ {
+ struct stat St;
+ if (stat(sourceslist.c_str(), &St) == 0 && St.st_size == 0)
+ RemoveFile("edit-sources", sourceslist);
+ }
+ else if (FileExists(sourceslist) && !before.VerifyFile(sourceslist))
+ {
+ file_changed = true;
+ pkgCacheFile::RemoveCaches();
+ }
+ pkgCacheFile CacheFile;
+ res = CacheFile.BuildCaches(nullptr);
+ if (res == false || _error->empty(GlobalError::WARNING) == false) {
+ std::string outs;
+ strprintf(outs, _("Failed to parse %s. Edit again? "), sourceslist.c_str());
+ // FIXME: should we add a "restore previous" option here?
+ if (YnPrompt(outs.c_str(), true) == false)
+ {
+ if (res == false && _error->PendingError() == false)
+ {
+ CacheFile.Close();
+ pkgCacheFile::RemoveCaches();
+ res = CacheFile.BuildCaches(nullptr);
+ }
+ break;
+ }
+ }
+ } while (res == false);
+
+ if (res == true && file_changed == true)
+ {
+ ioprintf(
+ std::cout, _("Your '%s' file changed, please run 'apt-get update'.\n"),
+ sourceslist.c_str());
+ }
+ return res;
+}
+ /*}}}*/
diff --git a/apt-private/private-sources.h b/apt-private/private-sources.h
new file mode 100644
index 0000000..0c42190
--- /dev/null
+++ b/apt-private/private-sources.h
@@ -0,0 +1,10 @@
+#ifndef APT_PRIVATE_SOURCES_H
+#define APT_PRIVATE_SOURCES_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool EditSources(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-unmet.cc b/apt-private/private-unmet.cc
new file mode 100644
index 0000000..f5161fc
--- /dev/null
+++ b/apt-private/private-unmet.cc
@@ -0,0 +1,120 @@
+// -*- mode: cpp; mode: fold -*-
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-unmet.h>
+
+#include <stddef.h>
+
+#include <iostream>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// UnMet - Show unmet dependencies /*{{{*/
+static bool ShowUnMet(pkgCache::VerIterator const &V, bool const Important)
+{
+ bool Header = false;
+ for (pkgCache::DepIterator D = V.DependsList(); D.end() == false;)
+ {
+ // Collect or groups
+ pkgCache::DepIterator Start;
+ pkgCache::DepIterator End;
+ D.GlobOr(Start,End);
+
+ // Important deps only
+ if (Important == true)
+ if (End->Type != pkgCache::Dep::PreDepends &&
+ End->Type != pkgCache::Dep::Depends)
+ continue;
+
+ // Skip conflicts and replaces
+ if (End.IsNegative() == true || End->Type == pkgCache::Dep::Replaces)
+ continue;
+
+ // Verify the or group
+ bool OK = false;
+ pkgCache::DepIterator RealStart = Start;
+ do
+ {
+ // See if this dep is Ok
+ pkgCache::Version **VList = Start.AllTargets();
+ if (*VList != 0)
+ {
+ OK = true;
+ delete [] VList;
+ break;
+ }
+ delete [] VList;
+
+ if (Start == End)
+ break;
+ ++Start;
+ }
+ while (1);
+
+ // The group is OK
+ if (OK == true)
+ continue;
+
+ // Oops, it failed..
+ if (Header == false)
+ ioprintf(std::cout,_("Package %s version %s has an unmet dep:\n"),
+ V.ParentPkg().FullName(true).c_str(),V.VerStr());
+ Header = true;
+
+ // Print out the dep type
+ std::cout << " " << End.DepType() << ": ";
+
+ // Show the group
+ Start = RealStart;
+ do
+ {
+ std::cout << Start.TargetPkg().FullName(true);
+ if (Start.TargetVer() != 0)
+ std::cout << " (" << Start.CompType() << " " << Start.TargetVer() <<
+ ")";
+ if (Start == End)
+ break;
+ std::cout << " | ";
+ ++Start;
+ }
+ while (1);
+
+ std::cout << std::endl;
+ }
+ return true;
+}
+bool UnMet(CommandLine &CmdL)
+{
+ bool const Important = _config->FindB("APT::Cache::Important",false);
+
+ pkgCacheFile CacheFile;
+ if (unlikely(CacheFile.GetPkgCache() == NULL))
+ return false;
+
+ if (CmdL.FileSize() <= 1)
+ {
+ for (pkgCache::PkgIterator P = CacheFile.GetPkgCache()->PkgBegin(); P.end() == false; ++P)
+ for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
+ if (ShowUnMet(V, Important) == false)
+ return false;
+ }
+ else
+ {
+ CacheSetHelperVirtuals helper(true, GlobalError::NOTICE);
+ APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1,
+ APT::CacheSetHelper::CANDIDATE, helper);
+ for (APT::VersionList::iterator V = verset.begin(); V != verset.end(); ++V)
+ if (ShowUnMet(V, Important) == false)
+ return false;
+ }
+ return true;
+}
+ /*}}}*/
diff --git a/apt-private/private-unmet.h b/apt-private/private-unmet.h
new file mode 100644
index 0000000..31c2c17
--- /dev/null
+++ b/apt-private/private-unmet.h
@@ -0,0 +1,10 @@
+#ifndef APT_PRIVATE_UNMET_H
+#define APT_PRIVATE_UNMET_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool UnMet(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-update.cc b/apt-private/private-update.cc
new file mode 100644
index 0000000..affae65
--- /dev/null
+++ b/apt-private/private-update.cc
@@ -0,0 +1,148 @@
+// Include files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/metaindex.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/update.h>
+
+#include <apt-private/acqprogress.h>
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-update.h>
+
+#include <ostream>
+#include <string>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// DoUpdate - Update the package lists /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DoUpdate(CommandLine &CmdL)
+{
+ if (CmdL.FileSize() != 1)
+ return _error->Error(_("The update command takes no arguments"));
+
+ CacheFile Cache;
+
+ // Get the source list
+ if (Cache.BuildSourceList() == false)
+ return false;
+ pkgSourceList *List = Cache.GetSourceList();
+
+ // Just print out the uris an exit if the --print-uris flag was used
+ if (_config->FindB("APT::Get::Print-URIs") == true)
+ {
+ // force a hashsum for compatibility reasons
+ _config->CndSet("Acquire::ForceHash", "md5sum");
+
+ // Populate it with the source selection and get all Indexes
+ // (GetAll=true)
+ aptAcquireWithTextStatus Fetcher;
+ if (List->GetIndexes(&Fetcher,true) == false)
+ return false;
+
+ std::string compExt = APT::Configuration::getCompressionTypes()[0];
+ pkgAcquire::UriIterator I = Fetcher.UriBegin();
+ for (; I != Fetcher.UriEnd(); ++I)
+ {
+ std::string FileName = flNotDir(I->Owner->DestFile);
+ if(compExt.empty() == false &&
+ APT::String::Endswith(FileName, compExt))
+ FileName = FileName.substr(0, FileName.size() - compExt.size() - 1);
+ c1out << '\'' << I->URI << "' " << FileName << ' ' <<
+ std::to_string(I->Owner->FileSize) << ' ' << I->Owner->HashSum() << std::endl;
+ }
+ return true;
+ }
+
+ // do the work
+ if (_config->FindB("APT::Get::Download",true) == true)
+ {
+ AcqTextStatus Stat(std::cout, ScreenWidth,_config->FindI("quiet",0));
+ ListUpdate(Stat, *List);
+ }
+
+ if (_config->FindB("pkgCacheFile::Generate", true) == false)
+ return true;
+
+ // Rebuild the cache.
+ pkgCacheFile::RemoveCaches();
+ if (Cache.BuildCaches(false) == false)
+ return false;
+
+ if (_config->FindB("APT::Get::Update::SourceListWarnings", true))
+ {
+ List = Cache.GetSourceList();
+ for (pkgSourceList::const_iterator S = List->begin(); S != List->end(); ++S)
+ {
+ if (APT::String::Startswith((*S)->GetURI(), "ftp://") == false)
+ continue;
+ pkgCache::RlsFileIterator const RlsFile = (*S)->FindInCache(Cache, false);
+ if (RlsFile.end() || RlsFile->Origin == 0 || RlsFile->Label == 0)
+ continue;
+ char const *const affected[][2] = {
+ {"Debian", "Debian"},
+ {"Debian", "Debian-Security"},
+ {"Debian Backports", "Debian Backports"},
+ };
+ auto const matchRelease = [&](decltype(affected[0]) a) {
+ return strcmp(RlsFile.Origin(), a[0]) == 0 && strcmp(RlsFile.Label(), a[1]) == 0;
+ };
+ if (std::find_if(std::begin(affected), std::end(affected), matchRelease) != std::end(affected))
+ _error->Warning("Debian shuts down public FTP services currently still used in your sources.list(5) as '%s'.\n"
+ "See press release %s for details.",
+ (*S)->GetURI().c_str(), "https://debian.org/News/2017/20170425");
+ }
+ for (pkgSourceList::const_iterator S = List->begin(); S != List->end(); ++S)
+ {
+ URI uri((*S)->GetURI());
+ if (uri.User.empty() && uri.Password.empty())
+ continue;
+ // we can't really predict if a +http method supports everything http does,
+ // so we play it safe and use an allowlist here.
+ char const *const affected[] = {"http", "https", "tor+http", "tor+https", "ftp"};
+ if (std::find(std::begin(affected), std::end(affected), uri.Access) != std::end(affected))
+ // TRANSLATOR: the first two are manpage references, the last the URI from a sources.list
+ _error->Notice(_("Usage of %s should be preferred over embedding login information directly in the %s entry for '%s'"),
+ "apt_auth.conf(5)", "sources.list(5)", URI::ArchiveOnly(uri).c_str());
+ }
+ }
+
+ // show basic stats (if the user whishes)
+ if (_config->FindB("APT::Cmd::Show-Update-Stats", false) == true)
+ {
+ int upgradable = 0;
+ if (Cache.Open(false) == false)
+ return false;
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() != true; ++I)
+ {
+ pkgDepCache::StateCache &state = Cache[I];
+ if (I->CurrentVer != 0 && state.Upgradable() && state.CandidateVer != NULL)
+ upgradable++;
+ }
+ const char *msg = P_(
+ "%i package can be upgraded. Run 'apt list --upgradable' to see it.\n",
+ "%i packages can be upgraded. Run 'apt list --upgradable' to see them.\n",
+ upgradable);
+ if (upgradable == 0)
+ c1out << _("All packages are up to date.") << std::endl;
+ else
+ ioprintf(c1out, msg, upgradable);
+
+ RunScripts("APT::Update::Post-Invoke-Stats");
+ }
+
+ return true;
+}
+ /*}}}*/
diff --git a/apt-private/private-update.h b/apt-private/private-update.h
new file mode 100644
index 0000000..e584f70
--- /dev/null
+++ b/apt-private/private-update.h
@@ -0,0 +1,10 @@
+#ifndef APT_PRIVATE_UPDATE_H
+#define APT_PRIVATE_UPDATE_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool DoUpdate(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-upgrade.cc b/apt-private/private-upgrade.cc
new file mode 100644
index 0000000..aeaf506
--- /dev/null
+++ b/apt-private/private-upgrade.cc
@@ -0,0 +1,66 @@
+// Includes /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/upgrade.h>
+
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-install.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-upgrade.h>
+
+#include <iostream>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// this is actually performing the various upgrade operations
+static bool UpgradeHelper(CommandLine &CmdL, int UpgradeFlags)
+{
+ CacheFile Cache;
+ auto VolatileCmdL = GetPseudoPackages(Cache.GetSourceList(), CmdL, AddVolatileBinaryFile, "");
+
+ if (Cache.OpenForInstall() == false || Cache.CheckDeps() == false)
+ return false;
+
+ if(!DoCacheManipulationFromCommandLine(CmdL, VolatileCmdL, Cache, UpgradeFlags))
+ return false;
+
+ return InstallPackages(Cache,true);
+}
+
+// DoDistUpgrade - Automatic smart upgrader /*{{{*/
+// ---------------------------------------------------------------------
+/* Intelligent upgrader that will install and remove packages at will */
+bool DoDistUpgrade(CommandLine &CmdL)
+{
+ return UpgradeHelper(CmdL, APT::Upgrade::ALLOW_EVERYTHING);
+}
+ /*}}}*/
+bool DoUpgrade(CommandLine &CmdL) /*{{{*/
+{
+ if (_config->FindB("APT::Get::Upgrade-Allow-New", false) == true)
+ return DoUpgradeWithAllowNewPackages(CmdL);
+ else
+ return DoUpgradeNoNewPackages(CmdL);
+}
+ /*}}}*/
+// DoUpgradeNoNewPackages - Upgrade all packages /*{{{*/
+// ---------------------------------------------------------------------
+/* Upgrade all packages without installing new packages or erasing old
+ packages */
+bool DoUpgradeNoNewPackages(CommandLine &CmdL)
+{
+ // Do the upgrade
+ return UpgradeHelper(CmdL,
+ APT::Upgrade::FORBID_REMOVE_PACKAGES|
+ APT::Upgrade::FORBID_INSTALL_NEW_PACKAGES);
+}
+ /*}}}*/
+// DoSafeUpgrade - Upgrade all packages with install but not remove /*{{{*/
+bool DoUpgradeWithAllowNewPackages(CommandLine &CmdL)
+{
+ return UpgradeHelper(CmdL, APT::Upgrade::FORBID_REMOVE_PACKAGES);
+}
+ /*}}}*/
diff --git a/apt-private/private-upgrade.h b/apt-private/private-upgrade.h
new file mode 100644
index 0000000..16bb93c
--- /dev/null
+++ b/apt-private/private-upgrade.h
@@ -0,0 +1,13 @@
+#ifndef APTPRIVATE_PRIVATE_UPGRADE_H
+#define APTPRIVATE_PRIVATE_UPGRADE_H
+
+#include <apt-pkg/macros.h>
+
+class CommandLine;
+
+APT_PUBLIC bool DoDistUpgrade(CommandLine &CmdL);
+APT_PUBLIC bool DoUpgrade(CommandLine &CmdL);
+bool DoUpgradeNoNewPackages(CommandLine &CmdL);
+bool DoUpgradeWithAllowNewPackages(CommandLine &CmdL);
+
+#endif
diff --git a/apt-private/private-utils.cc b/apt-private/private-utils.cc
new file mode 100644
index 0000000..5863925
--- /dev/null
+++ b/apt-private/private-utils.cc
@@ -0,0 +1,98 @@
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+
+#include <apt-private/private-utils.h>
+
+#include <cstdlib>
+#include <sstream>
+#include <unistd.h>
+
+// DisplayFileInPager - Display File with pager /*{{{*/
+bool DisplayFileInPager(std::string const &filename)
+{
+ pid_t Process = ExecFork();
+ if (Process == 0)
+ {
+ const char *Args[3];
+ Args[1] = filename.c_str();
+ Args[2] = NULL;
+ if (isatty(STDOUT_FILENO) == 1)
+ {
+ // likely installed, provided by sensible-utils
+ std::string const pager = _config->Find("Dir::Bin::Pager",
+ "sensible-pager");
+ Args[0] = pager.c_str();
+ execvp(Args[0],(char **)Args);
+ // lets try some obvious alternatives
+ Args[0] = getenv("PAGER");
+ if (Args[0] != NULL)
+ execvp(Args[0],(char **)Args);
+
+ Args[0] = "pager";
+ execvp(Args[0],(char **)Args);
+ }
+ // we could read the file ourselves, but… meh
+ Args[0] = "cat";
+ execvp(Args[0],(char **)Args);
+ exit(100);
+ }
+
+ // Wait for the subprocess
+ return ExecWait(Process, "pager", false);
+}
+ /*}}}*/
+// EditFileInSensibleEditor - Edit File with editor /*{{{*/
+bool EditFileInSensibleEditor(std::string const &filename)
+{
+ pid_t Process = ExecFork();
+ if (Process == 0)
+ {
+ // likely installed, provided by sensible-utils
+ std::string const editor = _config->Find("Dir::Bin::Editor",
+ "sensible-editor");
+ const char *Args[3];
+ Args[0] = editor.c_str();
+ Args[1] = filename.c_str();
+ Args[2] = NULL;
+ execvp(Args[0],(char **)Args);
+ // the usual suspects we can try as an alternative
+ Args[0] = getenv("VISUAL");
+ if (Args[0] != NULL)
+ execvp(Args[0],(char **)Args);
+
+ Args[0] = getenv("EDITOR");
+ if (Args[0] != NULL)
+ execvp(Args[0],(char **)Args);
+
+ Args[0] = "editor";
+ execvp(Args[0],(char **)Args);
+ exit(100);
+ }
+
+ // Wait for the subprocess
+ return ExecWait(Process, "editor", false);
+}
+ /*}}}*/
+time_t GetSecondsSinceEpoch() /*{{{*/
+{
+ auto const source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+ if (source_date_epoch == nullptr)
+ return time(nullptr);
+
+ time_t epoch;
+ std::stringstream ss(source_date_epoch);
+ ss >> epoch;
+
+ if (ss.fail() || !ss.eof())
+ {
+ _error->Warning("Environment variable SOURCE_DATE_EPOCH was ignored as it has an invalid value: \"%s\"",
+ source_date_epoch);
+ return time(nullptr);
+ }
+
+ return epoch;
+}
+ /*}}}*/
diff --git a/apt-private/private-utils.h b/apt-private/private-utils.h
new file mode 100644
index 0000000..4d48bd1
--- /dev/null
+++ b/apt-private/private-utils.h
@@ -0,0 +1,10 @@
+#ifndef APT_PRIVATE_UTILS_H
+#define APT_PRIVATE_UTILS_H
+
+#include <string>
+
+bool DisplayFileInPager(std::string const &filename);
+bool EditFileInSensibleEditor(std::string const &filename);
+time_t GetSecondsSinceEpoch();
+
+#endif