From d1027179026357c0db89c7b27dd8d774e34e574b Mon Sep 17 00:00:00 2001 From: Sam Bingner Date: Mon, 30 Dec 2019 16:31:01 -1000 Subject: Add support for JSON output --- apt-pkg/CMakeLists.txt | 3 +- apt-pkg/algorithms.cc | 1418 -------------------------------------- apt-pkg/algorithms.h | 6 + apt-pkg/algorithms.mm | 1519 +++++++++++++++++++++++++++++++++++++++++ apt-private/CMakeLists.txt | 4 +- apt-private/private-output.cc | 801 ---------------------- apt-private/private-output.mm | 956 ++++++++++++++++++++++++++ 7 files changed, 2485 insertions(+), 2222 deletions(-) delete mode 100644 apt-pkg/algorithms.cc create mode 100644 apt-pkg/algorithms.mm delete mode 100644 apt-private/private-output.cc create mode 100644 apt-private/private-output.mm diff --git a/apt-pkg/CMakeLists.txt b/apt-pkg/CMakeLists.txt index 318c6d798..5915a588c 100644 --- a/apt-pkg/CMakeLists.txt +++ b/apt-pkg/CMakeLists.txt @@ -32,7 +32,7 @@ set(APT_PKG_MAJOR ${MAJOR} PARENT_SCOPE) # exporting for methods/CMakeLists.txt # 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" "${CMAKE_CURRENT_BINARY_DIR}/tagfile-keys.cc") +file(GLOB_RECURSE library "*.cc" "*.mm" "${CMAKE_CURRENT_BINARY_DIR}/tagfile-keys.cc") file(GLOB_RECURSE headers "*.h") # Create a library using the C++ files @@ -61,6 +61,7 @@ target_link_libraries(apt-pkg $<$:${UDEV_LIBRARIES}> $<$:${SYSTEMD_LIBRARIES}> ${ICONV_LIBRARIES} + "-framework Foundation" ) set_target_properties(apt-pkg PROPERTIES VERSION ${MAJOR}.${MINOR}) set_target_properties(apt-pkg PROPERTIES SOVERSION ${MAJOR}) diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc deleted file mode 100644 index bb0e2f873..000000000 --- a/apt-pkg/algorithms.cc +++ /dev/null @@ -1,1418 +0,0 @@ -// -*- mode: cpp; mode: fold -*- -// Description /*{{{*/ -/* ###################################################################### - - Algorithms - A set of misc algorithms - - The pkgProblemResolver class has become insanely complex and - very sophisticated, it handles every test case I have thrown at it - to my satisfaction. Understanding exactly why all the steps the class - does are required is difficult and changing though not very risky - may result in other cases not working. - - ##################################################################### */ - /*}}}*/ -// Include Files /*{{{*/ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - /*}}}*/ -using namespace std; - -class APT_HIDDEN pkgSimulatePrivate -{ -public: - std::vector List; -}; -// Simulate::Simulate - Constructor /*{{{*/ -// --------------------------------------------------------------------- -/* The legacy translations here of input Pkg iterators is obsolete, - this is not necessary since the pkgCaches are fully shared now. */ -pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache), - d(new pkgSimulatePrivate()), iPolicy(Cache), - Sim(&Cache->GetCache(),&iPolicy), - group(Sim) -{ - Sim.Init(0); - auto PackageCount = Cache->Head().PackageCount; - Flags = new unsigned char[PackageCount]; - memset(Flags,0,sizeof(*Flags)*PackageCount); - - // Fake a filename so as not to activate the media swapping - string Jnk = "SIMULATE"; - for (decltype(PackageCount) I = 0; I != PackageCount; ++I) - FileNames[I] = Jnk; -} - /*}}}*/ -// Simulate::~Simulate - Destructor /*{{{*/ -pkgSimulate::~pkgSimulate() -{ - delete[] Flags; - delete d; -} - /*}}}*/ -// Simulate::Describe - Describe a package /*{{{*/ -// --------------------------------------------------------------------- -/* Parameter Current == true displays the current package version, - Parameter Candidate == true displays the candidate package version */ -void pkgSimulate::Describe(PkgIterator Pkg,ostream &out,bool Current,bool Candidate) -{ - VerIterator Ver(Sim); - - out << Pkg.FullName(true); - - if (Current == true) - { - Ver = Pkg.CurrentVer(); - if (Ver.end() == false) - out << " [" << Ver.VerStr() << ']'; - } - - if (Candidate == true) - { - Ver = Sim[Pkg].CandidateVerIter(Sim); - if (Ver.end() == true) - return; - - out << " (" << Ver.VerStr() << ' ' << Ver.RelStr() << ')'; - } -} - /*}}}*/ -// Simulate::Install - Simulate unpacking of a package /*{{{*/ -// --------------------------------------------------------------------- -/* */ -bool pkgSimulate::Install(PkgIterator iPkg,string File) -{ - if (iPkg.end() || File.empty()) - return false; - d->List.emplace_back(pkgDPkgPM::Item::Install, iPkg, File); - return true; -} -bool pkgSimulate::RealInstall(PkgIterator iPkg,string /*File*/) -{ - // Adapt the iterator - PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); - Flags[Pkg->ID] = 1; - - cout << "Inst "; - Describe(Pkg,cout,true,true); - Sim.MarkInstall(Pkg,false); - - // Look for broken conflicts+predepends. - for (PkgIterator I = Sim.PkgBegin(); I.end() == false; ++I) - { - if (Sim[I].InstallVer == 0) - continue; - - for (DepIterator D = Sim[I].InstVerIter(Sim).DependsList(); D.end() == false;) - { - DepIterator Start; - DepIterator End; - D.GlobOr(Start,End); - if (Start.IsNegative() == true || - End->Type == pkgCache::Dep::PreDepends) - { - if ((Sim[End] & pkgDepCache::DepGInstall) == 0) - { - cout << " [" << I.FullName(false) << " on " << Start.TargetPkg().FullName(false) << ']'; - if (Start->Type == pkgCache::Dep::Conflicts) - _error->Error("Fatal, conflicts violated %s",I.FullName(false).c_str()); - } - } - } - } - - if (Sim.BrokenCount() != 0) - ShortBreaks(); - else - cout << endl; - return true; -} - /*}}}*/ -// Simulate::Configure - Simulate configuration of a Package /*{{{*/ -// --------------------------------------------------------------------- -/* This is not an accurate simulation of relatity, we should really not - install the package.. For some investigations it may be necessary - however. */ -bool pkgSimulate::Configure(PkgIterator iPkg) -{ - if (iPkg.end()) - return false; - d->List.emplace_back(pkgDPkgPM::Item::Configure, iPkg); - return true; -} -bool pkgSimulate::RealConfigure(PkgIterator iPkg) -{ - // Adapt the iterator - PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); - - Flags[Pkg->ID] = 2; - - if (Sim[Pkg].InstBroken() == true) - { - cout << "Conf " << Pkg.FullName(false) << " broken" << endl; - - Sim.Update(); - - // Print out each package and the failed dependencies - for (pkgCache::DepIterator D = Sim[Pkg].InstVerIter(Sim).DependsList(); D.end() == false; ++D) - { - if (Sim.IsImportantDep(D) == false || - (Sim[D] & pkgDepCache::DepInstall) != 0) - continue; - - if (D->Type == pkgCache::Dep::Obsoletes) - cout << " Obsoletes:" << D.TargetPkg().FullName(false); - else if (D->Type == pkgCache::Dep::Conflicts) - cout << " Conflicts:" << D.TargetPkg().FullName(false); - else if (D->Type == pkgCache::Dep::DpkgBreaks) - cout << " Breaks:" << D.TargetPkg().FullName(false); - else - cout << " Depends:" << D.TargetPkg().FullName(false); - } - cout << endl; - - _error->Error("Conf Broken %s",Pkg.FullName(false).c_str()); - } - else - { - cout << "Conf "; - Describe(Pkg,cout,false,true); - } - - if (Sim.BrokenCount() != 0) - ShortBreaks(); - else - cout << endl; - - return true; -} - /*}}}*/ -// Simulate::Remove - Simulate the removal of a package /*{{{*/ -// --------------------------------------------------------------------- -/* */ -bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge) -{ - if (iPkg.end()) - return false; - d->List.emplace_back(Purge ? pkgDPkgPM::Item::Purge : pkgDPkgPM::Item::Remove, iPkg); - return true; -} -bool pkgSimulate::RealRemove(PkgIterator iPkg,bool Purge) -{ - // Adapt the iterator - PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); - if (Pkg.end() == true) - { - std::cerr << (Purge ? "Purg" : "Remv") << " invalid package " << iPkg.FullName() << std::endl; - return false; - } - - Flags[Pkg->ID] = 3; - Sim.MarkDelete(Pkg); - - if (Purge == true) - cout << "Purg "; - else - cout << "Remv "; - Describe(Pkg,cout,true,false); - - if (Sim.BrokenCount() != 0) - ShortBreaks(); - else - cout << endl; - - return true; -} - /*}}}*/ -// Simulate::ShortBreaks - Print out a short line describing all breaks /*{{{*/ -// --------------------------------------------------------------------- -/* */ -void pkgSimulate::ShortBreaks() -{ - cout << " ["; - for (PkgIterator I = Sim.PkgBegin(); I.end() == false; ++I) - { - if (Sim[I].InstBroken() == true) - { - if (Flags[I->ID] == 0) - cout << I.FullName(false) << ' '; -/* else - cout << I.Name() << "! ";*/ - } - } - cout << ']' << endl; -} - /*}}}*/ -bool pkgSimulate::Go2(APT::Progress::PackageManager *) /*{{{*/ -{ - if (pkgDPkgPM::ExpandPendingCalls(d->List, Cache) == false) - return false; - for (auto && I : d->List) - switch (I.Op) - { - case pkgDPkgPM::Item::Install: - if (RealInstall(I.Pkg, I.File) == false) - return false; - break; - case pkgDPkgPM::Item::Configure: - if (RealConfigure(I.Pkg) == false) - return false; - break; - case pkgDPkgPM::Item::Remove: - if (RealRemove(I.Pkg, false) == false) - return false; - break; - case pkgDPkgPM::Item::Purge: - if (RealRemove(I.Pkg, true) == false) - return false; - break; - case pkgDPkgPM::Item::ConfigurePending: - case pkgDPkgPM::Item::TriggersPending: - case pkgDPkgPM::Item::RemovePending: - case pkgDPkgPM::Item::PurgePending: - return _error->Error("Internal error, simulation encountered unexpected pending item"); - } - return true; -} - /*}}}*/ -// ApplyStatus - Adjust for non-ok packages /*{{{*/ -// --------------------------------------------------------------------- -/* We attempt to change the state of the all packages that have failed - installation toward their real state. The ordering code will perform - the necessary calculations to deal with the problems. */ -bool pkgApplyStatus(pkgDepCache &Cache) -{ - pkgDepCache::ActionGroup group(Cache); - - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (I->VersionList == 0) - continue; - - // Only choice for a ReInstReq package is to reinstall - if (I->InstState == pkgCache::State::ReInstReq || - I->InstState == pkgCache::State::HoldReInstReq) - { - if (I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) - Cache.MarkKeep(I, false, false); - else - { - // Is this right? Will dpkg choke on an upgrade? - if (Cache[I].CandidateVer != 0 && - Cache[I].CandidateVerIter(Cache).Downloadable() == true) - Cache.MarkInstall(I, false, 0, false); - else - return _error->Error(_("The package %s needs to be reinstalled, " - "but I can't find an archive for it."),I.FullName(true).c_str()); - } - - continue; - } - - switch (I->CurrentState) - { - /* This means installation failed somehow - it does not need to be - re-unpacked (probably) */ - case pkgCache::State::UnPacked: - case pkgCache::State::HalfConfigured: - case pkgCache::State::TriggersAwaited: - case pkgCache::State::TriggersPending: - if ((I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) || - I.State() != pkgCache::PkgIterator::NeedsUnpack) - Cache.MarkKeep(I, false, false); - else - { - if (Cache[I].CandidateVer != 0 && - Cache[I].CandidateVerIter(Cache).Downloadable() == true) - Cache.MarkInstall(I, true, 0, false); - else - Cache.MarkDelete(I, false, 0, false); - } - break; - - // This means removal failed - case pkgCache::State::HalfInstalled: - Cache.MarkDelete(I, false, 0, false); - break; - - default: - if (I->InstState != pkgCache::State::Ok) - return _error->Error("The package %s is not ok and I " - "don't know how to fix it!",I.FullName(false).c_str()); - } - } - return true; -} - /*}}}*/ -// FixBroken - Fix broken packages /*{{{*/ -// --------------------------------------------------------------------- -/* This autoinstalls every broken package and then runs the problem resolver - on the result. */ -bool pkgFixBroken(pkgDepCache &Cache) -{ - pkgDepCache::ActionGroup group(Cache); - - // Auto upgrade all broken packages - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if (Cache[I].NowBroken() == true) - Cache.MarkInstall(I, true, 0, false); - - /* Fix packages that are in a NeedArchive state but don't have a - downloadable install version */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (I.State() != pkgCache::PkgIterator::NeedsUnpack || - Cache[I].Delete() == true) - continue; - - if (Cache[I].InstVerIter(Cache).Downloadable() == false) - continue; - - Cache.MarkInstall(I, true, 0, false); - } - - pkgProblemResolver Fix(&Cache); - return Fix.Resolve(true); -} - /*}}}*/ -// ProblemResolver::pkgProblemResolver - Constructor /*{{{*/ -// --------------------------------------------------------------------- -/* */ -pkgProblemResolver::pkgProblemResolver(pkgDepCache *pCache) : d(NULL), Cache(*pCache) -{ - // Allocate memory - auto const Size = Cache.Head().PackageCount; - Scores = new int[Size]; - Flags = new unsigned char[Size]; - memset(Flags,0,sizeof(*Flags)*Size); - - // Set debug to true to see its decision logic - Debug = _config->FindB("Debug::pkgProblemResolver",false); -} - /*}}}*/ -// ProblemResolver::~pkgProblemResolver - Destructor /*{{{*/ -// --------------------------------------------------------------------- -/* */ -pkgProblemResolver::~pkgProblemResolver() -{ - delete [] Scores; - delete [] Flags; -} - /*}}}*/ -// ProblemResolver::ScoreSort - Sort the list by score /*{{{*/ -// --------------------------------------------------------------------- -/* */ -int pkgProblemResolver::ScoreSort(Package const *A,Package const *B) -{ - if (Scores[A->ID] > Scores[B->ID]) - return -1; - if (Scores[A->ID] < Scores[B->ID]) - return 1; - return 0; -} - /*}}}*/ -// ProblemResolver::MakeScores - Make the score table /*{{{*/ -// --------------------------------------------------------------------- -/* */ -void pkgProblemResolver::MakeScores() -{ - auto const Size = Cache.Head().PackageCount; - memset(Scores,0,sizeof(*Scores)*Size); - - // maps to pkgCache::State::VerPriority: - // Required Important Standard Optional Extra - int PrioMap[] = { - 0, - _config->FindI("pkgProblemResolver::Scores::Required",3), - _config->FindI("pkgProblemResolver::Scores::Important",2), - _config->FindI("pkgProblemResolver::Scores::Standard",1), - _config->FindI("pkgProblemResolver::Scores::Optional",-1), - _config->FindI("pkgProblemResolver::Scores::Extra",-2) - }; - int PrioEssentials = _config->FindI("pkgProblemResolver::Scores::Essentials",100); - int PrioInstalledAndNotObsolete = _config->FindI("pkgProblemResolver::Scores::NotObsolete",1); - int DepMap[] = { - 0, - _config->FindI("pkgProblemResolver::Scores::Depends",1), - _config->FindI("pkgProblemResolver::Scores::PreDepends",1), - _config->FindI("pkgProblemResolver::Scores::Suggests",0), - _config->FindI("pkgProblemResolver::Scores::Recommends",1), - _config->FindI("pkgProblemResolver::Scores::Conflicts",-1), - _config->FindI("pkgProblemResolver::Scores::Replaces",0), - _config->FindI("pkgProblemResolver::Scores::Obsoletes",0), - _config->FindI("pkgProblemResolver::Scores::Breaks",-1), - _config->FindI("pkgProblemResolver::Scores::Enhances",0) - }; - int AddProtected = _config->FindI("pkgProblemResolver::Scores::AddProtected",10000); - int AddEssential = _config->FindI("pkgProblemResolver::Scores::AddEssential",5000); - - if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true) - clog << "Settings used to calculate pkgProblemResolver::Scores::" << endl - << " Required => " << PrioMap[pkgCache::State::Required] << endl - << " Important => " << PrioMap[pkgCache::State::Important] << endl - << " Standard => " << PrioMap[pkgCache::State::Standard] << endl - << " Optional => " << PrioMap[pkgCache::State::Optional] << endl - << " Extra => " << PrioMap[pkgCache::State::Extra] << endl - << " Essentials => " << PrioEssentials << endl - << " InstalledAndNotObsolete => " << PrioInstalledAndNotObsolete << endl - << " Pre-Depends => " << DepMap[pkgCache::Dep::PreDepends] << endl - << " Depends => " << DepMap[pkgCache::Dep::Depends] << endl - << " Recommends => " << DepMap[pkgCache::Dep::Recommends] << endl - << " Suggests => " << DepMap[pkgCache::Dep::Suggests] << endl - << " Conflicts => " << DepMap[pkgCache::Dep::Conflicts] << endl - << " Breaks => " << DepMap[pkgCache::Dep::DpkgBreaks] << endl - << " Replaces => " << DepMap[pkgCache::Dep::Replaces] << endl - << " Obsoletes => " << DepMap[pkgCache::Dep::Obsoletes] << endl - << " Enhances => " << DepMap[pkgCache::Dep::Enhances] << endl - << " AddProtected => " << AddProtected << endl - << " AddEssential => " << AddEssential << endl; - - // Generate the base scores for a package based on its properties - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (Cache[I].InstallVer == 0) - continue; - - int &Score = Scores[I->ID]; - - /* This is arbitrary, it should be high enough to elevate an - essantial package above most other packages but low enough - to allow an obsolete essential packages to be removed by - a conflicts on a powerful normal package (ie libc6) */ - if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential - || (I->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) - Score += PrioEssentials; - - pkgCache::VerIterator const InstVer = Cache[I].InstVerIter(Cache); - // We apply priorities only to downloadable packages, all others are prio:extra - // as an obsolete prio:standard package can't be that standard anymore… - if (InstVer->Priority <= pkgCache::State::Extra && InstVer.Downloadable() == true) - Score += PrioMap[InstVer->Priority]; - else - Score += PrioMap[pkgCache::State::Extra]; - - /* This helps to fix oddball problems with conflicting packages - on the same level. We enhance the score of installed packages - if those are not obsolete */ - if (I->CurrentVer != 0 && Cache[I].CandidateVer != 0 && Cache[I].CandidateVerIter(Cache).Downloadable()) - Score += PrioInstalledAndNotObsolete; - - // propagate score points along dependencies - for (pkgCache::DepIterator D = InstVer.DependsList(); D.end() == false; ++D) - { - if (DepMap[D->Type] == 0) - continue; - pkgCache::PkgIterator const T = D.TargetPkg(); - if (D->Version != 0) - { - pkgCache::VerIterator const IV = Cache[T].InstVerIter(Cache); - if (IV.end() == true || D.IsSatisfied(IV) == false) - continue; - } - Scores[T->ID] += DepMap[D->Type]; - } - } - - // Copy the scores to advoid additive looping - std::unique_ptr OldScores(new int[Size]); - memcpy(OldScores.get(),Scores,sizeof(*Scores)*Size); - - /* Now we cause 1 level of dependency inheritance, that is we add the - score of the packages that depend on the target Package. This - fortifies high scoring packages */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (Cache[I].InstallVer == 0) - continue; - - for (pkgCache::DepIterator D = I.RevDependsList(); D.end() == false; ++D) - { - // Only do it for the install version - if ((pkgCache::Version *)D.ParentVer() != Cache[D.ParentPkg()].InstallVer || - (D->Type != pkgCache::Dep::Depends && - D->Type != pkgCache::Dep::PreDepends && - D->Type != pkgCache::Dep::Recommends)) - continue; - - // Do not propagate negative scores otherwise - // an extra (-2) package might score better than an optional (-1) - if (OldScores[D.ParentPkg()->ID] > 0) - Scores[I->ID] += OldScores[D.ParentPkg()->ID]; - } - } - - /* Now we propagate along provides. This makes the packages that - provide important packages extremely important */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - for (pkgCache::PrvIterator P = I.ProvidesList(); P.end() == false; ++P) - { - // Only do it once per package - if ((pkgCache::Version *)P.OwnerVer() != Cache[P.OwnerPkg()].InstallVer) - continue; - Scores[P.OwnerPkg()->ID] += abs(Scores[I->ID] - OldScores[I->ID]); - } - } - - /* Protected things are pushed really high up. This number should put them - ahead of everything */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if ((Flags[I->ID] & Protected) != 0) - Scores[I->ID] += AddProtected; - if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential || - (I->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) - Scores[I->ID] += AddEssential; - } -} - /*}}}*/ -// ProblemResolver::DoUpgrade - Attempt to upgrade this package /*{{{*/ -// --------------------------------------------------------------------- -/* This goes through and tries to reinstall packages to make this package - installable */ -bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) -{ - pkgDepCache::ActionGroup group(Cache); - - if ((Flags[Pkg->ID] & Upgradable) == 0 || Cache[Pkg].Upgradable() == false) - return false; - if ((Flags[Pkg->ID] & Protected) == Protected) - return false; - - Flags[Pkg->ID] &= ~Upgradable; - - bool WasKept = Cache[Pkg].Keep(); - Cache.MarkInstall(Pkg, false, 0, false); - - // This must be a virtual package or something like that. - if (Cache[Pkg].InstVerIter(Cache).end() == true) - return false; - - // Isolate the problem dependency - bool Fail = false; - for (pkgCache::DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); D.end() == false;) - { - // Compute a single dependency element (glob or) - pkgCache::DepIterator Start = D; - pkgCache::DepIterator End = D; - for (bool LastOR = true; D.end() == false && LastOR == true;) - { - LastOR = (D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or; - ++D; - if (LastOR == true) - End = D; - } - - // We only worry about critical deps. - if (End.IsCritical() != true) - continue; - - // Iterate over all the members in the or group - while (1) - { - // Dep is ok now - if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) - break; - - // Do not change protected packages - PkgIterator P = Start.SmartTargetPkg(); - if ((Flags[P->ID] & Protected) == Protected) - { - if (Debug == true) - clog << " Reinst Failed because of protected " << P.FullName(false) << endl; - Fail = true; - } - else - { - // Upgrade the package if the candidate version will fix the problem. - if ((Cache[Start] & pkgDepCache::DepCVer) == pkgDepCache::DepCVer) - { - if (DoUpgrade(P) == false) - { - if (Debug == true) - clog << " Reinst Failed because of " << P.FullName(false) << endl; - Fail = true; - } - else - { - Fail = false; - break; - } - } - else - { - /* We let the algorithm deal with conflicts on its next iteration, - it is much smarter than us */ - if (Start.IsNegative() == true) - break; - - if (Debug == true) - clog << " Reinst Failed early because of " << Start.TargetPkg().FullName(false) << endl; - Fail = true; - } - } - - if (Start == End) - break; - ++Start; - } - if (Fail == true) - break; - } - - // Undo our operations - it might be smart to undo everything this did.. - if (Fail == true) - { - if (WasKept == true) - Cache.MarkKeep(Pkg, false, false); - else - Cache.MarkDelete(Pkg, false, 0, false); - return false; - } - - if (Debug == true) - clog << " Re-Instated " << Pkg.FullName(false) << endl; - return true; -} - /*}}}*/ -// ProblemResolver::Resolve - calls a resolver to fix the situation /*{{{*/ -bool pkgProblemResolver::Resolve(bool BrokenFix, OpProgress * const Progress) -{ - std::string const solver = _config->Find("APT::Solver", "internal"); - auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, 0, Progress); - if (solver != "internal") - return ret; - return ResolveInternal(BrokenFix); -} - /*}}}*/ -// ProblemResolver::ResolveInternal - Run the resolution pass /*{{{*/ -// --------------------------------------------------------------------- -/* This routines works by calculating a score for each package. The score - is derived by considering the package's priority and all reverse - dependents giving an integer that reflects the amount of breakage that - adjusting the package will inflict. - - It goes from highest score to lowest and corrects all of the breaks by - keeping or removing the dependent packages. If that fails then it removes - the package itself and goes on. The routine should be able to intelligently - go from any broken state to a fixed state. - - The BrokenFix flag enables a mode where the algorithm tries to - upgrade packages to advoid problems. */ -bool pkgProblemResolver::ResolveInternal(bool const BrokenFix) -{ - pkgDepCache::ActionGroup group(Cache); - - // Record which packages are marked for install - bool Again = false; - do - { - Again = false; - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (Cache[I].Install() == true) - Flags[I->ID] |= PreInstalled; - else - { - if (Cache[I].InstBroken() == true && BrokenFix == true) - { - Cache.MarkInstall(I, false, 0, false); - if (Cache[I].Install() == true) - Again = true; - } - - Flags[I->ID] &= ~PreInstalled; - } - Flags[I->ID] |= Upgradable; - } - } - while (Again == true); - - if (Debug == true) { - clog << "Starting pkgProblemResolver with broken count: " - << Cache.BrokenCount() << endl; - } - - MakeScores(); - - auto const Size = Cache.Head().PackageCount; - - /* We have to order the packages so that the broken fixing pass - operates from highest score to lowest. This prevents problems when - high score packages cause the removal of lower score packages that - would cause the removal of even lower score packages. */ - std::unique_ptr PList(new pkgCache::Package *[Size]); - pkgCache::Package **PEnd = PList.get(); - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - *PEnd++ = I; - - std::sort(PList.get(), PEnd, [this](Package *a, Package *b) { return ScoreSort(a, b) < 0; }); - - if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true) - { - clog << "Show Scores" << endl; - for (pkgCache::Package **K = PList.get(); K != PEnd; K++) - if (Scores[(*K)->ID] != 0) - { - pkgCache::PkgIterator Pkg(Cache,*K); - clog << Scores[(*K)->ID] << ' ' << APT::PrettyPkg(&Cache, Pkg) << std::endl; - } - } - - if (Debug == true) { - clog << "Starting 2 pkgProblemResolver with broken count: " - << Cache.BrokenCount() << endl; - } - - /* Now consider all broken packages. For each broken package we either - remove the package or fix it's problem. We do this once, it should - not be possible for a loop to form (that is a < b < c and fixing b by - changing a breaks c) */ - bool Change = true; - bool const TryFixByInstall = _config->FindB("pkgProblemResolver::FixByInstall", true); - std::vector KillList; - for (int Counter = 0; Counter != 10 && Change == true; Counter++) - { - Change = false; - for (pkgCache::Package **K = PList.get(); K != PEnd; K++) - { - pkgCache::PkgIterator I(Cache,*K); - - /* We attempt to install this and see if any breaks result, - this takes care of some strange cases */ - if (Cache[I].CandidateVer != Cache[I].InstallVer && - I->CurrentVer != 0 && Cache[I].InstallVer != 0 && - (Flags[I->ID] & PreInstalled) != 0 && - (Flags[I->ID] & Protected) == 0 && - (Flags[I->ID] & ReInstateTried) == 0) - { - if (Debug == true) - clog << " Try to Re-Instate (" << Counter << ") " << I.FullName(false) << endl; - auto const OldBreaks = Cache.BrokenCount(); - pkgCache::Version *OldVer = Cache[I].InstallVer; - Flags[I->ID] &= ReInstateTried; - - Cache.MarkInstall(I, false, 0, false); - if (Cache[I].InstBroken() == true || - OldBreaks < Cache.BrokenCount()) - { - if (OldVer == 0) - Cache.MarkDelete(I, false, 0, false); - else - Cache.MarkKeep(I, false, false); - } - else - if (Debug == true) - clog << "Re-Instated " << I.FullName(false) << " (" << OldBreaks << " vs " << Cache.BrokenCount() << ')' << endl; - } - - if (Cache[I].InstallVer == 0 || Cache[I].InstBroken() == false) - continue; - - if (Debug == true) - clog << "Investigating (" << Counter << ") " << APT::PrettyPkg(&Cache, I) << endl; - - // Isolate the problem dependency - bool InOr = false; - pkgCache::DepIterator Start; - pkgCache::DepIterator End; - size_t OldSize = 0; - - KillList.resize(0); - - enum {OrRemove,OrKeep} OrOp = OrRemove; - for (pkgCache::DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); - D.end() == false || InOr == true;) - { - // Compute a single dependency element (glob or) - if (Start == End) - { - // Decide what to do - if (InOr == true && OldSize == KillList.size()) - { - if (OrOp == OrRemove) - { - if ((Flags[I->ID] & Protected) != Protected) - { - if (Debug == true) - clog << " Or group remove for " << I.FullName(false) << endl; - Cache.MarkDelete(I, false, 0, false); - Change = true; - } - } - else if (OrOp == OrKeep) - { - if (Debug == true) - clog << " Or group keep for " << I.FullName(false) << endl; - Cache.MarkKeep(I, false, false); - Change = true; - } - } - - /* We do an extra loop (as above) to finalize the or group - processing */ - InOr = false; - OrOp = OrRemove; - D.GlobOr(Start,End); - if (Start.end() == true) - break; - - // We only worry about critical deps. - if (End.IsCritical() != true) - continue; - - InOr = Start != End; - OldSize = KillList.size(); - } - else - { - ++Start; - // We only worry about critical deps. - if (Start.IsCritical() != true) - continue; - } - - // Dep is ok - if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) - { - InOr = false; - continue; - } - - if (Debug == true) - clog << "Broken " << APT::PrettyDep(&Cache, Start) << endl; - - /* Look across the version list. If there are no possible - targets then we keep the package and bail. This is necessary - if a package has a dep on another package that can't be found */ - std::unique_ptr VList(Start.AllTargets()); - if (VList[0] == 0 && (Flags[I->ID] & Protected) != Protected && - Start.IsNegative() == false && - Cache[I].NowBroken() == false) - { - if (InOr == true) - { - /* No keep choice because the keep being OK could be the - result of another element in the OR group! */ - continue; - } - - Change = true; - Cache.MarkKeep(I, false, false); - break; - } - - bool Done = false; - for (pkgCache::Version **V = VList.get(); *V != 0; V++) - { - pkgCache::VerIterator Ver(Cache,*V); - pkgCache::PkgIterator Pkg = Ver.ParentPkg(); - - /* This is a conflicts, and the version we are looking - at is not the currently selected version of the - package, which means it is not necessary to - remove/keep */ - if (Cache[Pkg].InstallVer != Ver && Start.IsNegative() == true) - { - if (Debug) - clog << " Conflicts//Breaks against version " - << Ver.VerStr() << " for " << Pkg.Name() - << " but that is not InstVer, ignoring" - << endl; - continue; - } - - if (Debug == true) - clog << " Considering " << Pkg.FullName(false) << ' ' << Scores[Pkg->ID] << - " as a solution to " << I.FullName(false) << ' ' << Scores[I->ID] << endl; - - /* Try to fix the package under consideration rather than - fiddle with the VList package */ - if (Scores[I->ID] <= Scores[Pkg->ID] || - ((Cache[Start] & pkgDepCache::DepNow) == 0 && - End.IsNegative() == false)) - { - // Try a little harder to fix protected packages.. - if ((Flags[I->ID] & Protected) == Protected) - { - if (DoUpgrade(Pkg) == true) - { - if (Scores[Pkg->ID] > Scores[I->ID]) - Scores[Pkg->ID] = Scores[I->ID]; - break; - } - - continue; - } - - /* See if a keep will do, unless the package is protected, - then installing it will be necessary */ - bool Installed = Cache[I].Install(); - Cache.MarkKeep(I, false, false); - if (Cache[I].InstBroken() == false) - { - // Unwind operation will be keep now - if (OrOp == OrRemove) - OrOp = OrKeep; - - // Restore - if (InOr == true && Installed == true) - Cache.MarkInstall(I, false, 0, false); - - if (Debug == true) - clog << " Holding Back " << I.FullName(false) << " rather than change " << Start.TargetPkg().FullName(false) << endl; - } - else - { - if (BrokenFix == false || DoUpgrade(I) == false) - { - // Consider other options - if (InOr == false || Cache[I].Garbage == true) - { - if (Debug == true) - clog << " Removing " << I.FullName(false) << " rather than change " << Start.TargetPkg().FullName(false) << endl; - Cache.MarkDelete(I, false, 0, false); - if (Counter > 1 && Scores[Pkg->ID] > Scores[I->ID]) - Scores[I->ID] = Scores[Pkg->ID]; - } - else if (TryFixByInstall == true && - Start.TargetPkg()->CurrentVer == 0 && - Cache[Start.TargetPkg()].Delete() == false && - (Flags[Start.TargetPkg()->ID] & ToRemove) != ToRemove && - Cache.GetCandidateVersion(Start.TargetPkg()).end() == false) - { - /* Before removing or keeping the package with the broken dependency - try instead to install the first not previously installed package - solving this dependency. This helps every time a previous solver - is removed by the resolver because of a conflict or alike but it is - dangerous as it could trigger new breaks/conflicts… */ - if (Debug == true) - clog << " Try Installing " << APT::PrettyPkg(&Cache, Start.TargetPkg()) << " before changing " << I.FullName(false) << std::endl; - auto const OldBroken = Cache.BrokenCount(); - Cache.MarkInstall(Start.TargetPkg(), true, 1, false); - // FIXME: we should undo the complete MarkInstall process here - if (Cache[Start.TargetPkg()].InstBroken() == true || Cache.BrokenCount() > OldBroken) - Cache.MarkDelete(Start.TargetPkg(), false, 1, false); - } - } - } - - Change = true; - Done = true; - break; - } - else - { - if (Start->Type == pkgCache::Dep::DpkgBreaks) - { - // first, try upgradring the package, if that - // does not help, the breaks goes onto the - // kill list - // - // FIXME: use DoUpgrade(Pkg) instead? - if (Cache[End] & pkgDepCache::DepGCVer) - { - if (Debug) - clog << " Upgrading " << Pkg.FullName(false) << " due to Breaks field in " << I.FullName(false) << endl; - Cache.MarkInstall(Pkg, false, 0, false); - continue; - } - } - - // Skip adding to the kill list if it is protected - if ((Flags[Pkg->ID] & Protected) != 0) - continue; - - if (Debug == true) - clog << " Added " << Pkg.FullName(false) << " to the remove list" << endl; - - KillList.push_back({Pkg, End}); - - if (Start.IsNegative() == false) - break; - } - } - - // Hm, nothing can possibly satisfy this dep. Nuke it. - if (VList[0] == 0 && - Start.IsNegative() == false && - (Flags[I->ID] & Protected) != Protected) - { - bool Installed = Cache[I].Install(); - Cache.MarkKeep(I); - if (Cache[I].InstBroken() == false) - { - // Unwind operation will be keep now - if (OrOp == OrRemove) - OrOp = OrKeep; - - // Restore - if (InOr == true && Installed == true) - Cache.MarkInstall(I, false, 0, false); - - if (Debug == true) - clog << " Holding Back " << I.FullName(false) << " because I can't find " << Start.TargetPkg().FullName(false) << endl; - } - else - { - if (Debug == true) - clog << " Removing " << I.FullName(false) << " because I can't find " << Start.TargetPkg().FullName(false) << endl; - if (InOr == false) - Cache.MarkDelete(I, false, 0, false); - } - - Change = true; - Done = true; - } - - // Try some more - if (InOr == true) - continue; - - if (Done == true) - break; - } - - // Apply the kill list now - if (Cache[I].InstallVer != 0) - { - for (auto J = KillList.begin(); J != KillList.end(); J++) - { - Change = true; - if ((Cache[J->Dep] & pkgDepCache::DepGNow) == 0) - { - if (J->Dep.IsNegative() == true) - { - if (Debug == true) - clog << " Fixing " << I.FullName(false) << " via remove of " << J->Pkg.FullName(false) << endl; - Cache.MarkDelete(J->Pkg, false, 0, false); - } - } - else - { - if (Debug == true) - clog << " Fixing " << I.FullName(false) << " via keep of " << J->Pkg.FullName(false) << endl; - Cache.MarkKeep(J->Pkg, false, false); - } - - if (Counter > 1) - { - if (Scores[I->ID] > Scores[J->Pkg->ID]) - Scores[J->Pkg->ID] = Scores[I->ID]; - } - } - } - } - } - - if (Debug == true) - clog << "Done" << endl; - - if (Cache.BrokenCount() != 0) - { - // See if this is the result of a hold - pkgCache::PkgIterator I = Cache.PkgBegin(); - for (;I.end() != true; ++I) - { - if (Cache[I].InstBroken() == false) - continue; - if ((Flags[I->ID] & Protected) != Protected) - return _error->Error(_("Error, pkgProblemResolver::Resolve generated breaks, this may be caused by held packages.")); - } - return _error->Error(_("Unable to correct problems, you have held broken packages.")); - } - - // set the auto-flags (mvo: I'm not sure if we _really_ need this) - pkgCache::PkgIterator I = Cache.PkgBegin(); - for (;I.end() != true; ++I) { - if (Cache[I].NewInstall() && !(Flags[I->ID] & PreInstalled)) { - if(_config->FindB("Debug::pkgAutoRemove",false)) { - std::clog << "Resolve installed new pkg: " << I.FullName(false) - << " (now marking it as auto)" << std::endl; - } - Cache[I].Flags |= pkgCache::Flag::Auto; - } - } - - - return true; -} - /*}}}*/ -// ProblemResolver::BreaksInstOrPolicy - Check if the given pkg is broken/*{{{*/ -// --------------------------------------------------------------------- -/* This checks if the given package is broken either by a hard dependency - (InstBroken()) or by introducing a new policy breakage e.g. new - unsatisfied recommends for a package that was in "policy-good" state - - Note that this is not perfect as it will ignore further breakage - for already broken policy (recommends) -*/ -bool pkgProblemResolver::InstOrNewPolicyBroken(pkgCache::PkgIterator I) -{ - // a broken install is always a problem - if (Cache[I].InstBroken() == true) - { - if (Debug == true) - std::clog << " Dependencies are not satisfied for " << APT::PrettyPkg(&Cache, I) << std::endl; - return true; - } - - // a newly broken policy (recommends/suggests) is a problem - if (Cache[I].NowPolicyBroken() == false && - Cache[I].InstPolicyBroken() == true) - { - if (Debug == true) - std::clog << " Policy breaks with upgrade of " << APT::PrettyPkg(&Cache, I) << std::endl; - return true; - } - - return false; -} - /*}}}*/ -// ProblemResolver::ResolveByKeep - Resolve problems using keep /*{{{*/ -// --------------------------------------------------------------------- -/* This is the work horse of the soft upgrade routine. It is very gentle - in that it does not install or remove any packages. It is assumed that the - system was non-broken previously. */ -bool pkgProblemResolver::ResolveByKeep(OpProgress * const Progress) -{ - std::string const solver = _config->Find("APT::Solver", "internal"); - constexpr auto flags = EDSP::Request::UPGRADE_ALL | EDSP::Request::FORBID_NEW_INSTALL | EDSP::Request::FORBID_REMOVE; - auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, flags, Progress); - if (solver != "internal") - return ret; - return ResolveByKeepInternal(); -} - /*}}}*/ -// ProblemResolver::ResolveByKeepInternal - Resolve problems using keep /*{{{*/ -// --------------------------------------------------------------------- -/* This is the work horse of the soft upgrade routine. It is very gentle - in that it does not install or remove any packages. It is assumed that the - system was non-broken previously. */ -bool pkgProblemResolver::ResolveByKeepInternal() -{ - pkgDepCache::ActionGroup group(Cache); - - MakeScores(); - - /* We have to order the packages so that the broken fixing pass - operates from highest score to lowest. This prevents problems when - high score packages cause the removal of lower score packages that - would cause the removal of even lower score packages. */ - auto Size = Cache.Head().PackageCount; - pkgCache::Package **PList = new pkgCache::Package *[Size]; - pkgCache::Package **PEnd = PList; - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - *PEnd++ = I; - - std::sort(PList,PEnd,[this](Package *a, Package *b) { return ScoreSort(a, b) < 0; }); - - - if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true) - { - clog << "Show Scores" << endl; - for (pkgCache::Package **K = PList; K != PEnd; K++) - if (Scores[(*K)->ID] != 0) - { - pkgCache::PkgIterator Pkg(Cache,*K); - clog << Scores[(*K)->ID] << ' ' << APT::PrettyPkg(&Cache, Pkg) << std::endl; - } - } - - if (Debug == true) - clog << "Entering ResolveByKeep" << endl; - - // Consider each broken package - pkgCache::Package **LastStop = 0; - for (pkgCache::Package **K = PList; K != PEnd; K++) - { - pkgCache::PkgIterator I(Cache,*K); - - if (Cache[I].InstallVer == 0) - continue; - - if (InstOrNewPolicyBroken(I) == false) - continue; - - /* Keep the package. If this works then great, otherwise we have - to be significantly more aggressive and manipulate its dependencies */ - if ((Flags[I->ID] & Protected) == 0) - { - if (Debug == true) - clog << "Keeping package " << I.FullName(false) << endl; - Cache.MarkKeep(I, false, false); - if (InstOrNewPolicyBroken(I) == false) - { - K = PList - 1; - continue; - } - } - - // Isolate the problem dependencies - for (pkgCache::DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); D.end() == false;) - { - DepIterator Start; - DepIterator End; - D.GlobOr(Start,End); - - // We only worry about critical deps. - if (End.IsCritical() != true) - continue; - - // Dep is ok - if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) - continue; - - /* Hm, the group is broken.. I suppose the best thing to do is to - is to try every combination of keep/not-keep for the set, but that's - slow, and this never happens, just be conservative and assume the - list of ors is in preference and keep till it starts to work. */ - while (true) - { - if (Debug == true) - clog << "Package " << I.FullName(false) << " " << APT::PrettyDep(&Cache, Start) << endl; - - // Look at all the possible provides on this package - std::unique_ptr VList(Start.AllTargets()); - for (pkgCache::Version **V = VList.get(); *V != 0; V++) - { - pkgCache::VerIterator Ver(Cache,*V); - pkgCache::PkgIterator Pkg = Ver.ParentPkg(); - - // It is not keepable - if (Cache[Pkg].InstallVer == 0 || - Pkg->CurrentVer == 0) - continue; - - if ((Flags[I->ID] & Protected) == 0) - { - if (Debug == true) - clog << " Keeping Package " << Pkg.FullName(false) << " due to " << Start.DepType() << endl; - Cache.MarkKeep(Pkg, false, false); - } - - if (InstOrNewPolicyBroken(I) == false) - break; - } - - if (InstOrNewPolicyBroken(I) == false) - break; - - if (Start == End) - break; - ++Start; - } - - if (InstOrNewPolicyBroken(I) == false) - break; - } - - if (InstOrNewPolicyBroken(I) == true) - continue; - - // Restart again. - if (K == LastStop) { - // I is an iterator based off our temporary package list, - // so copy the name we need before deleting the temporary list - std::string const LoopingPackage = I.FullName(false); - delete[] PList; - return _error->Error("Internal Error, pkgProblemResolver::ResolveByKeep is looping on package %s.", LoopingPackage.c_str()); - } - LastStop = K; - K = PList - 1; - } - - delete[] PList; - return true; -} - /*}}}*/ -// ProblemResolver::InstallProtect - deprecated cpu-eating no-op /*{{{*/ -// --------------------------------------------------------------------- -/* Actions issued with FromUser bit set are protected from further - modification (expect by other calls with FromUser set) nowadays , so we - don't need to reissue actions here, they are already set in stone. */ -void pkgProblemResolver::InstallProtect() -{ - pkgDepCache::ActionGroup group(Cache); - - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if ((Flags[I->ID] & Protected) == Protected) - { - if ((Flags[I->ID] & ToRemove) == ToRemove) - Cache.MarkDelete(I); - else - { - // preserve the information whether the package was auto - // or manually installed - bool autoInst = (Cache[I].Flags & pkgCache::Flag::Auto); - Cache.MarkInstall(I, false, 0, !autoInst); - } - } - } -} - /*}}}*/ -// PrioSortList - Sort a list of versions by priority /*{{{*/ -// --------------------------------------------------------------------- -/* This is meant to be used in conjunction with AllTargets to get a list - of versions ordered by preference. */ - -struct PrioComp { - pkgCache &PrioCache; - - explicit PrioComp(pkgCache &PrioCache) : PrioCache(PrioCache) { - } - - bool operator() (pkgCache::Version * const &A, pkgCache::Version * const &B) { - return compare(A, B) < 0; - } - - int compare(pkgCache::Version * const &A, pkgCache::Version * const &B) { - pkgCache::VerIterator L(PrioCache,A); - pkgCache::VerIterator R(PrioCache,B); - - if ((L.ParentPkg()->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential && - (R.ParentPkg()->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential) - return 1; - if ((L.ParentPkg()->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential && - (R.ParentPkg()->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) - return -1; - - if ((L.ParentPkg()->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important && - (R.ParentPkg()->Flags & pkgCache::Flag::Important) != pkgCache::Flag::Important) - return 1; - if ((L.ParentPkg()->Flags & pkgCache::Flag::Important) != pkgCache::Flag::Important && - (R.ParentPkg()->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) - return -1; - - if (L->Priority != R->Priority) - return R->Priority - L->Priority; - return strcmp(L.ParentPkg().Name(),R.ParentPkg().Name()); - } -}; - -void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List) -{ - unsigned long Count = 0; - for (pkgCache::Version **I = List; *I != 0; I++) - Count++; - std::sort(List,List+Count,PrioComp(Cache)); -} - /*}}}*/ diff --git a/apt-pkg/algorithms.h b/apt-pkg/algorithms.h index c5c9f4c69..5ee5e5f62 100644 --- a/apt-pkg/algorithms.h +++ b/apt-pkg/algorithms.h @@ -48,6 +48,9 @@ using std::ostream; #include #include #endif +#ifdef __OBJC__ +#import +#endif class pkgSimulatePrivate; @@ -87,6 +90,9 @@ public: private: APT_HIDDEN void ShortBreaks(); APT_HIDDEN void Describe(PkgIterator iPkg,std::ostream &out,bool Current,bool Candidate); +#ifdef __OBJC__ + APT_HIDDEN NSMutableDictionary * DescribeJSON(PkgIterator iPkg,bool Current,bool Candidate); +#endif APT_HIDDEN bool RealInstall(PkgIterator Pkg,std::string File); APT_HIDDEN bool RealConfigure(PkgIterator Pkg); APT_HIDDEN bool RealRemove(PkgIterator Pkg,bool Purge); diff --git a/apt-pkg/algorithms.mm b/apt-pkg/algorithms.mm new file mode 100644 index 000000000..4ac63419b --- /dev/null +++ b/apt-pkg/algorithms.mm @@ -0,0 +1,1519 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ###################################################################### + + Algorithms - A set of misc algorithms + + The pkgProblemResolver class has become insanely complex and + very sophisticated, it handles every test case I have thrown at it + to my satisfaction. Understanding exactly why all the steps the class + does are required is difficult and changing though not very risky + may result in other cases not working. + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +NSMutableArray *simulatedOperations = nil; + /*}}}*/ +using namespace std; + +class APT_HIDDEN pkgSimulatePrivate +{ +public: + std::vector List; +}; +// Simulate::Simulate - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* The legacy translations here of input Pkg iterators is obsolete, + this is not necessary since the pkgCaches are fully shared now. */ +pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache), + d(new pkgSimulatePrivate()), iPolicy(Cache), + Sim(&Cache->GetCache(),&iPolicy), + group(Sim) +{ + Sim.Init(0); + auto PackageCount = Cache->Head().PackageCount; + Flags = new unsigned char[PackageCount]; + memset(Flags,0,sizeof(*Flags)*PackageCount); + + // Fake a filename so as not to activate the media swapping + string Jnk = "SIMULATE"; + for (decltype(PackageCount) I = 0; I != PackageCount; ++I) + FileNames[I] = Jnk; +} + /*}}}*/ +// Simulate::~Simulate - Destructor /*{{{*/ +pkgSimulate::~pkgSimulate() +{ + delete[] Flags; + delete d; +} + /*}}}*/ +// Simulate::Describe - Describe a package /*{{{*/ +// --------------------------------------------------------------------- +/* Parameter Current == true displays the current package version, + Parameter Candidate == true displays the candidate package version */ +void pkgSimulate::Describe(PkgIterator Pkg,ostream &out,bool Current,bool Candidate) +{ + VerIterator Ver(Sim); + + out << Pkg.FullName(true); + + if (Current == true) + { + Ver = Pkg.CurrentVer(); + if (Ver.end() == false) + out << " [" << Ver.VerStr() << ']'; + } + + if (Candidate == true) + { + Ver = Sim[Pkg].CandidateVerIter(Sim); + if (Ver.end() == true) + return; + + out << " (" << Ver.VerStr() << ' ' << Ver.RelStr() << ')'; + } +} + /*}}}*/ +// Simulate::DescribeJSON - Describe a package /*{{{*/ +// --------------------------------------------------------------------- +/* Parameter Current == true displays the current package version, + Parameter Candidate == true displays the candidate package version */ +NSMutableDictionary * pkgSimulate::DescribeJSON(PkgIterator Pkg,bool Current,bool Candidate) +{ + VerIterator Ver(Sim); + + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + dictionary[@"Package"] = @(Pkg.FullName(true).c_str()); + + if (Current == true) + { + Ver = Pkg.CurrentVer(); + if (Ver.end() == false){ + dictionary[@"CurrentVersion"] = @(Ver.VerStr()); + } + } + + if (Candidate == true) + { + Ver = Sim[Pkg].CandidateVerIter(Sim); + if (Ver.end() == true) + return dictionary; + dictionary[@"CandidateVersion"] = @(Ver.VerStr()); + dictionary[@"Release"] = @(Ver.RelStr().c_str()); + } + + return dictionary; +} + /*}}}*/ +// Simulate::Install - Simulate unpacking of a package /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSimulate::Install(PkgIterator iPkg,string File) +{ + if (iPkg.end() || File.empty()) + return false; + d->List.emplace_back(pkgDPkgPM::Item::Install, iPkg, File); + return true; +} +bool pkgSimulate::RealInstall(PkgIterator iPkg,string /*File*/) +{ + if (_config->FindB("APT::Format::JSON", false)){ + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); + Flags[Pkg->ID] = 1; + + NSMutableDictionary *package = DescribeJSON(Pkg, true, true); + package[@"Type"] = @"Inst"; + [simulatedOperations addObject:package]; + + Sim.MarkInstall(Pkg,false); + + // Look for broken conflicts+predepends. + for (PkgIterator I = Sim.PkgBegin(); I.end() == false; ++I) + { + if (Sim[I].InstallVer == 0) + continue; + + for (DepIterator D = Sim[I].InstVerIter(Sim).DependsList(); D.end() == false;) + { + DepIterator Start; + DepIterator End; + D.GlobOr(Start,End); + if (Start.IsNegative() == true || End->Type == pkgCache::Dep::PreDepends) + { + if ((Sim[End] & pkgDepCache::DepGInstall) == 0) + { + if (Start->Type == pkgCache::Dep::Conflicts) + _error->Error("Fatal, conflicts violated %s",I.FullName(false).c_str()); + } + } + } + } + return true; + } + + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); + Flags[Pkg->ID] = 1; + + cout << "Inst "; + Describe(Pkg,cout,true,true); + Sim.MarkInstall(Pkg,false); + + // Look for broken conflicts+predepends. + for (PkgIterator I = Sim.PkgBegin(); I.end() == false; ++I) + { + if (Sim[I].InstallVer == 0) + continue; + + for (DepIterator D = Sim[I].InstVerIter(Sim).DependsList(); D.end() == false;) + { + DepIterator Start; + DepIterator End; + D.GlobOr(Start,End); + if (Start.IsNegative() == true || + End->Type == pkgCache::Dep::PreDepends) + { + if ((Sim[End] & pkgDepCache::DepGInstall) == 0) + { + cout << " [" << I.FullName(false) << " on " << Start.TargetPkg().FullName(false) << ']'; + if (Start->Type == pkgCache::Dep::Conflicts) + _error->Error("Fatal, conflicts violated %s",I.FullName(false).c_str()); + } + } + } + } + + if (Sim.BrokenCount() != 0) + ShortBreaks(); + else + cout << endl; + return true; +} + /*}}}*/ +// Simulate::Configure - Simulate configuration of a Package /*{{{*/ +// --------------------------------------------------------------------- +/* This is not an accurate simulation of relatity, we should really not + install the package.. For some investigations it may be necessary + however. */ +bool pkgSimulate::Configure(PkgIterator iPkg) +{ + if (iPkg.end()) + return false; + d->List.emplace_back(pkgDPkgPM::Item::Configure, iPkg); + return true; +} +bool pkgSimulate::RealConfigure(PkgIterator iPkg) +{ + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); + + Flags[Pkg->ID] = 2; + + if (_config->FindB("APT::Format::JSON", false)){ + NSMutableDictionary *package = DescribeJSON(Pkg, false, true); + package[@"Type"] = @"Conf"; + [simulatedOperations addObject:package]; + } else { + if (Sim[Pkg].InstBroken() == true) + { + cout << "Conf " << Pkg.FullName(false) << " broken" << endl; + + Sim.Update(); + + // Print out each package and the failed dependencies + for (pkgCache::DepIterator D = Sim[Pkg].InstVerIter(Sim).DependsList(); D.end() == false; ++D) + { + if (Sim.IsImportantDep(D) == false || + (Sim[D] & pkgDepCache::DepInstall) != 0) + continue; + + if (D->Type == pkgCache::Dep::Obsoletes) + cout << " Obsoletes:" << D.TargetPkg().FullName(false); + else if (D->Type == pkgCache::Dep::Conflicts) + cout << " Conflicts:" << D.TargetPkg().FullName(false); + else if (D->Type == pkgCache::Dep::DpkgBreaks) + cout << " Breaks:" << D.TargetPkg().FullName(false); + else + cout << " Depends:" << D.TargetPkg().FullName(false); + } + cout << endl; + + _error->Error("Conf Broken %s",Pkg.FullName(false).c_str()); + } + else + { + cout << "Conf "; + Describe(Pkg,cout,false,true); + } + + if (Sim.BrokenCount() != 0) + ShortBreaks(); + else + cout << endl; + } + + return true; +} + /*}}}*/ +// Simulate::Remove - Simulate the removal of a package /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge) +{ + if (iPkg.end()) + return false; + d->List.emplace_back(Purge ? pkgDPkgPM::Item::Purge : pkgDPkgPM::Item::Remove, iPkg); + return true; +} +bool pkgSimulate::RealRemove(PkgIterator iPkg,bool Purge) +{ + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); + if (Pkg.end() == true) + { + std::cerr << (Purge ? "Purg" : "Remv") << " invalid package " << iPkg.FullName() << std::endl; + return false; + } + + Flags[Pkg->ID] = 3; + Sim.MarkDelete(Pkg); + + if (_config->FindB("APT::Format::JSON", false)){ + NSMutableDictionary *package = DescribeJSON(Pkg, true, false); + if (Purge == true) + package[@"Type"] = @"Purg"; + else + package[@"Type"] = @"Remv"; + + [simulatedOperations addObject:package]; + } else { + if (Purge == true) + cout << "Purg "; + else + cout << "Remv "; + Describe(Pkg,cout,true,false); + + if (Sim.BrokenCount() != 0) + ShortBreaks(); + else + cout << endl; + } + + return true; +} + /*}}}*/ +// Simulate::ShortBreaks - Print out a short line describing all breaks /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgSimulate::ShortBreaks() +{ + cout << " ["; + for (PkgIterator I = Sim.PkgBegin(); I.end() == false; ++I) + { + if (Sim[I].InstBroken() == true) + { + if (Flags[I->ID] == 0) + cout << I.FullName(false) << ' '; +/* else + cout << I.Name() << "! ";*/ + } + } + cout << ']' << endl; +} + /*}}}*/ +bool pkgSimulate::Go2(APT::Progress::PackageManager *) /*{{{*/ +{ + bool rv = true; + if (pkgDPkgPM::ExpandPendingCalls(d->List, Cache) == false) + return false; + BOOL doJSON = _config->FindB("APT::Format::JSON", false); + if (doJSON) { + simulatedOperations = [NSMutableArray array]; + } else { + simulatedOperations = nil; + } + for (auto && I : d->List) + switch (I.Op) + { + case pkgDPkgPM::Item::Install: + if (RealInstall(I.Pkg, I.File) == false) + goto failout; + break; + case pkgDPkgPM::Item::Configure: + if (RealConfigure(I.Pkg) == false) + goto failout; + break; + case pkgDPkgPM::Item::Remove: + if (RealRemove(I.Pkg, false) == false) + goto failout; + break; + case pkgDPkgPM::Item::Purge: + if (RealRemove(I.Pkg, true) == false) + goto failout; + break; + case pkgDPkgPM::Item::ConfigurePending: + case pkgDPkgPM::Item::TriggersPending: + case pkgDPkgPM::Item::RemovePending: + case pkgDPkgPM::Item::PurgePending: + return _error->Error("Internal error, simulation encountered unexpected pending item"); + } + if (false) { +failout: + rv = false; + } + if (doJSON && simulatedOperations != nil) { + NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"operations": simulatedOperations} options:NSJSONWritingPrettyPrinted error:nil]; + simulatedOperations = nil; + NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput]; + [stdout writeData:data]; + cout << endl << flush; + } + return rv; +} + /*}}}*/ +// ApplyStatus - Adjust for non-ok packages /*{{{*/ +// --------------------------------------------------------------------- +/* We attempt to change the state of the all packages that have failed + installation toward their real state. The ordering code will perform + the necessary calculations to deal with the problems. */ +bool pkgApplyStatus(pkgDepCache &Cache) +{ + pkgDepCache::ActionGroup group(Cache); + + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (I->VersionList == 0) + continue; + + // Only choice for a ReInstReq package is to reinstall + if (I->InstState == pkgCache::State::ReInstReq || + I->InstState == pkgCache::State::HoldReInstReq) + { + if (I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) + Cache.MarkKeep(I, false, false); + else + { + // Is this right? Will dpkg choke on an upgrade? + if (Cache[I].CandidateVer != 0 && + Cache[I].CandidateVerIter(Cache).Downloadable() == true) + Cache.MarkInstall(I, false, 0, false); + else + return _error->Error(_("The package %s needs to be reinstalled, " + "but I can't find an archive for it."),I.FullName(true).c_str()); + } + + continue; + } + + switch (I->CurrentState) + { + /* This means installation failed somehow - it does not need to be + re-unpacked (probably) */ + case pkgCache::State::UnPacked: + case pkgCache::State::HalfConfigured: + case pkgCache::State::TriggersAwaited: + case pkgCache::State::TriggersPending: + if ((I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) || + I.State() != pkgCache::PkgIterator::NeedsUnpack) + Cache.MarkKeep(I, false, false); + else + { + if (Cache[I].CandidateVer != 0 && + Cache[I].CandidateVerIter(Cache).Downloadable() == true) + Cache.MarkInstall(I, true, 0, false); + else + Cache.MarkDelete(I, false, 0, false); + } + break; + + // This means removal failed + case pkgCache::State::HalfInstalled: + Cache.MarkDelete(I, false, 0, false); + break; + + default: + if (I->InstState != pkgCache::State::Ok) + return _error->Error("The package %s is not ok and I " + "don't know how to fix it!",I.FullName(false).c_str()); + } + } + return true; +} + /*}}}*/ +// FixBroken - Fix broken packages /*{{{*/ +// --------------------------------------------------------------------- +/* This autoinstalls every broken package and then runs the problem resolver + on the result. */ +bool pkgFixBroken(pkgDepCache &Cache) +{ + pkgDepCache::ActionGroup group(Cache); + + // Auto upgrade all broken packages + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if (Cache[I].NowBroken() == true) + Cache.MarkInstall(I, true, 0, false); + + /* Fix packages that are in a NeedArchive state but don't have a + downloadable install version */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (I.State() != pkgCache::PkgIterator::NeedsUnpack || + Cache[I].Delete() == true) + continue; + + if (Cache[I].InstVerIter(Cache).Downloadable() == false) + continue; + + Cache.MarkInstall(I, true, 0, false); + } + + pkgProblemResolver Fix(&Cache); + return Fix.Resolve(true); +} + /*}}}*/ +// ProblemResolver::pkgProblemResolver - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgProblemResolver::pkgProblemResolver(pkgDepCache *pCache) : d(NULL), Cache(*pCache) +{ + // Allocate memory + auto const Size = Cache.Head().PackageCount; + Scores = new int[Size]; + Flags = new unsigned char[Size]; + memset(Flags,0,sizeof(*Flags)*Size); + + // Set debug to true to see its decision logic + Debug = _config->FindB("Debug::pkgProblemResolver",false); +} + /*}}}*/ +// ProblemResolver::~pkgProblemResolver - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgProblemResolver::~pkgProblemResolver() +{ + delete [] Scores; + delete [] Flags; +} + /*}}}*/ +// ProblemResolver::ScoreSort - Sort the list by score /*{{{*/ +// --------------------------------------------------------------------- +/* */ +int pkgProblemResolver::ScoreSort(Package const *A,Package const *B) +{ + if (Scores[A->ID] > Scores[B->ID]) + return -1; + if (Scores[A->ID] < Scores[B->ID]) + return 1; + return 0; +} + /*}}}*/ +// ProblemResolver::MakeScores - Make the score table /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgProblemResolver::MakeScores() +{ + auto const Size = Cache.Head().PackageCount; + memset(Scores,0,sizeof(*Scores)*Size); + + // maps to pkgCache::State::VerPriority: + // Required Important Standard Optional Extra + int PrioMap[] = { + 0, + _config->FindI("pkgProblemResolver::Scores::Required",3), + _config->FindI("pkgProblemResolver::Scores::Important",2), + _config->FindI("pkgProblemResolver::Scores::Standard",1), + _config->FindI("pkgProblemResolver::Scores::Optional",-1), + _config->FindI("pkgProblemResolver::Scores::Extra",-2) + }; + int PrioEssentials = _config->FindI("pkgProblemResolver::Scores::Essentials",100); + int PrioInstalledAndNotObsolete = _config->FindI("pkgProblemResolver::Scores::NotObsolete",1); + int DepMap[] = { + 0, + _config->FindI("pkgProblemResolver::Scores::Depends",1), + _config->FindI("pkgProblemResolver::Scores::PreDepends",1), + _config->FindI("pkgProblemResolver::Scores::Suggests",0), + _config->FindI("pkgProblemResolver::Scores::Recommends",1), + _config->FindI("pkgProblemResolver::Scores::Conflicts",-1), + _config->FindI("pkgProblemResolver::Scores::Replaces",0), + _config->FindI("pkgProblemResolver::Scores::Obsoletes",0), + _config->FindI("pkgProblemResolver::Scores::Breaks",-1), + _config->FindI("pkgProblemResolver::Scores::Enhances",0) + }; + int AddProtected = _config->FindI("pkgProblemResolver::Scores::AddProtected",10000); + int AddEssential = _config->FindI("pkgProblemResolver::Scores::AddEssential",5000); + + if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true) + clog << "Settings used to calculate pkgProblemResolver::Scores::" << endl + << " Required => " << PrioMap[pkgCache::State::Required] << endl + << " Important => " << PrioMap[pkgCache::State::Important] << endl + << " Standard => " << PrioMap[pkgCache::State::Standard] << endl + << " Optional => " << PrioMap[pkgCache::State::Optional] << endl + << " Extra => " << PrioMap[pkgCache::State::Extra] << endl + << " Essentials => " << PrioEssentials << endl + << " InstalledAndNotObsolete => " << PrioInstalledAndNotObsolete << endl + << " Pre-Depends => " << DepMap[pkgCache::Dep::PreDepends] << endl + << " Depends => " << DepMap[pkgCache::Dep::Depends] << endl + << " Recommends => " << DepMap[pkgCache::Dep::Recommends] << endl + << " Suggests => " << DepMap[pkgCache::Dep::Suggests] << endl + << " Conflicts => " << DepMap[pkgCache::Dep::Conflicts] << endl + << " Breaks => " << DepMap[pkgCache::Dep::DpkgBreaks] << endl + << " Replaces => " << DepMap[pkgCache::Dep::Replaces] << endl + << " Obsoletes => " << DepMap[pkgCache::Dep::Obsoletes] << endl + << " Enhances => " << DepMap[pkgCache::Dep::Enhances] << endl + << " AddProtected => " << AddProtected << endl + << " AddEssential => " << AddEssential << endl; + + // Generate the base scores for a package based on its properties + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (Cache[I].InstallVer == 0) + continue; + + int &Score = Scores[I->ID]; + + /* This is arbitrary, it should be high enough to elevate an + essantial package above most other packages but low enough + to allow an obsolete essential packages to be removed by + a conflicts on a powerful normal package (ie libc6) */ + if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential + || (I->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) + Score += PrioEssentials; + + pkgCache::VerIterator const InstVer = Cache[I].InstVerIter(Cache); + // We apply priorities only to downloadable packages, all others are prio:extra + // as an obsolete prio:standard package can't be that standard anymore… + if (InstVer->Priority <= pkgCache::State::Extra && InstVer.Downloadable() == true) + Score += PrioMap[InstVer->Priority]; + else + Score += PrioMap[pkgCache::State::Extra]; + + /* This helps to fix oddball problems with conflicting packages + on the same level. We enhance the score of installed packages + if those are not obsolete */ + if (I->CurrentVer != 0 && Cache[I].CandidateVer != 0 && Cache[I].CandidateVerIter(Cache).Downloadable()) + Score += PrioInstalledAndNotObsolete; + + // propagate score points along dependencies + for (pkgCache::DepIterator D = InstVer.DependsList(); D.end() == false; ++D) + { + if (DepMap[D->Type] == 0) + continue; + pkgCache::PkgIterator const T = D.TargetPkg(); + if (D->Version != 0) + { + pkgCache::VerIterator const IV = Cache[T].InstVerIter(Cache); + if (IV.end() == true || D.IsSatisfied(IV) == false) + continue; + } + Scores[T->ID] += DepMap[D->Type]; + } + } + + // Copy the scores to advoid additive looping + std::unique_ptr OldScores(new int[Size]); + memcpy(OldScores.get(),Scores,sizeof(*Scores)*Size); + + /* Now we cause 1 level of dependency inheritance, that is we add the + score of the packages that depend on the target Package. This + fortifies high scoring packages */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (Cache[I].InstallVer == 0) + continue; + + for (pkgCache::DepIterator D = I.RevDependsList(); D.end() == false; ++D) + { + // Only do it for the install version + if ((pkgCache::Version *)D.ParentVer() != Cache[D.ParentPkg()].InstallVer || + (D->Type != pkgCache::Dep::Depends && + D->Type != pkgCache::Dep::PreDepends && + D->Type != pkgCache::Dep::Recommends)) + continue; + + // Do not propagate negative scores otherwise + // an extra (-2) package might score better than an optional (-1) + if (OldScores[D.ParentPkg()->ID] > 0) + Scores[I->ID] += OldScores[D.ParentPkg()->ID]; + } + } + + /* Now we propagate along provides. This makes the packages that + provide important packages extremely important */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + for (pkgCache::PrvIterator P = I.ProvidesList(); P.end() == false; ++P) + { + // Only do it once per package + if ((pkgCache::Version *)P.OwnerVer() != Cache[P.OwnerPkg()].InstallVer) + continue; + Scores[P.OwnerPkg()->ID] += abs(Scores[I->ID] - OldScores[I->ID]); + } + } + + /* Protected things are pushed really high up. This number should put them + ahead of everything */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if ((Flags[I->ID] & Protected) != 0) + Scores[I->ID] += AddProtected; + if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential || + (I->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) + Scores[I->ID] += AddEssential; + } +} + /*}}}*/ +// ProblemResolver::DoUpgrade - Attempt to upgrade this package /*{{{*/ +// --------------------------------------------------------------------- +/* This goes through and tries to reinstall packages to make this package + installable */ +bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) +{ + pkgDepCache::ActionGroup group(Cache); + + if ((Flags[Pkg->ID] & Upgradable) == 0 || Cache[Pkg].Upgradable() == false) + return false; + if ((Flags[Pkg->ID] & Protected) == Protected) + return false; + + Flags[Pkg->ID] &= ~Upgradable; + + bool WasKept = Cache[Pkg].Keep(); + Cache.MarkInstall(Pkg, false, 0, false); + + // This must be a virtual package or something like that. + if (Cache[Pkg].InstVerIter(Cache).end() == true) + return false; + + // Isolate the problem dependency + bool Fail = false; + for (pkgCache::DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); D.end() == false;) + { + // Compute a single dependency element (glob or) + pkgCache::DepIterator Start = D; + pkgCache::DepIterator End = D; + for (bool LastOR = true; D.end() == false && LastOR == true;) + { + LastOR = (D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or; + ++D; + if (LastOR == true) + End = D; + } + + // We only worry about critical deps. + if (End.IsCritical() != true) + continue; + + // Iterate over all the members in the or group + while (1) + { + // Dep is ok now + if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) + break; + + // Do not change protected packages + PkgIterator P = Start.SmartTargetPkg(); + if ((Flags[P->ID] & Protected) == Protected) + { + if (Debug == true) + clog << " Reinst Failed because of protected " << P.FullName(false) << endl; + Fail = true; + } + else + { + // Upgrade the package if the candidate version will fix the problem. + if ((Cache[Start] & pkgDepCache::DepCVer) == pkgDepCache::DepCVer) + { + if (DoUpgrade(P) == false) + { + if (Debug == true) + clog << " Reinst Failed because of " << P.FullName(false) << endl; + Fail = true; + } + else + { + Fail = false; + break; + } + } + else + { + /* We let the algorithm deal with conflicts on its next iteration, + it is much smarter than us */ + if (Start.IsNegative() == true) + break; + + if (Debug == true) + clog << " Reinst Failed early because of " << Start.TargetPkg().FullName(false) << endl; + Fail = true; + } + } + + if (Start == End) + break; + ++Start; + } + if (Fail == true) + break; + } + + // Undo our operations - it might be smart to undo everything this did.. + if (Fail == true) + { + if (WasKept == true) + Cache.MarkKeep(Pkg, false, false); + else + Cache.MarkDelete(Pkg, false, 0, false); + return false; + } + + if (Debug == true) + clog << " Re-Instated " << Pkg.FullName(false) << endl; + return true; +} + /*}}}*/ +// ProblemResolver::Resolve - calls a resolver to fix the situation /*{{{*/ +bool pkgProblemResolver::Resolve(bool BrokenFix, OpProgress * const Progress) +{ + std::string const solver = _config->Find("APT::Solver", "internal"); + auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, 0, Progress); + if (solver != "internal") + return ret; + return ResolveInternal(BrokenFix); +} + /*}}}*/ +// ProblemResolver::ResolveInternal - Run the resolution pass /*{{{*/ +// --------------------------------------------------------------------- +/* This routines works by calculating a score for each package. The score + is derived by considering the package's priority and all reverse + dependents giving an integer that reflects the amount of breakage that + adjusting the package will inflict. + + It goes from highest score to lowest and corrects all of the breaks by + keeping or removing the dependent packages. If that fails then it removes + the package itself and goes on. The routine should be able to intelligently + go from any broken state to a fixed state. + + The BrokenFix flag enables a mode where the algorithm tries to + upgrade packages to advoid problems. */ +bool pkgProblemResolver::ResolveInternal(bool const BrokenFix) +{ + pkgDepCache::ActionGroup group(Cache); + + // Record which packages are marked for install + bool Again = false; + do + { + Again = false; + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (Cache[I].Install() == true) + Flags[I->ID] |= PreInstalled; + else + { + if (Cache[I].InstBroken() == true && BrokenFix == true) + { + Cache.MarkInstall(I, false, 0, false); + if (Cache[I].Install() == true) + Again = true; + } + + Flags[I->ID] &= ~PreInstalled; + } + Flags[I->ID] |= Upgradable; + } + } + while (Again == true); + + if (Debug == true) { + clog << "Starting pkgProblemResolver with broken count: " + << Cache.BrokenCount() << endl; + } + + MakeScores(); + + auto const Size = Cache.Head().PackageCount; + + /* We have to order the packages so that the broken fixing pass + operates from highest score to lowest. This prevents problems when + high score packages cause the removal of lower score packages that + would cause the removal of even lower score packages. */ + std::unique_ptr PList(new pkgCache::Package *[Size]); + pkgCache::Package **PEnd = PList.get(); + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + *PEnd++ = I; + + std::sort(PList.get(), PEnd, [this](Package *a, Package *b) { return ScoreSort(a, b) < 0; }); + + if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true) + { + clog << "Show Scores" << endl; + for (pkgCache::Package **K = PList.get(); K != PEnd; K++) + if (Scores[(*K)->ID] != 0) + { + pkgCache::PkgIterator Pkg(Cache,*K); + clog << Scores[(*K)->ID] << ' ' << APT::PrettyPkg(&Cache, Pkg) << std::endl; + } + } + + if (Debug == true) { + clog << "Starting 2 pkgProblemResolver with broken count: " + << Cache.BrokenCount() << endl; + } + + /* Now consider all broken packages. For each broken package we either + remove the package or fix it's problem. We do this once, it should + not be possible for a loop to form (that is a < b < c and fixing b by + changing a breaks c) */ + bool Change = true; + bool const TryFixByInstall = _config->FindB("pkgProblemResolver::FixByInstall", true); + std::vector KillList; + for (int Counter = 0; Counter != 10 && Change == true; Counter++) + { + Change = false; + for (pkgCache::Package **K = PList.get(); K != PEnd; K++) + { + pkgCache::PkgIterator I(Cache,*K); + + /* We attempt to install this and see if any breaks result, + this takes care of some strange cases */ + if (Cache[I].CandidateVer != Cache[I].InstallVer && + I->CurrentVer != 0 && Cache[I].InstallVer != 0 && + (Flags[I->ID] & PreInstalled) != 0 && + (Flags[I->ID] & Protected) == 0 && + (Flags[I->ID] & ReInstateTried) == 0) + { + if (Debug == true) + clog << " Try to Re-Instate (" << Counter << ") " << I.FullName(false) << endl; + auto const OldBreaks = Cache.BrokenCount(); + pkgCache::Version *OldVer = Cache[I].InstallVer; + Flags[I->ID] &= ReInstateTried; + + Cache.MarkInstall(I, false, 0, false); + if (Cache[I].InstBroken() == true || + OldBreaks < Cache.BrokenCount()) + { + if (OldVer == 0) + Cache.MarkDelete(I, false, 0, false); + else + Cache.MarkKeep(I, false, false); + } + else + if (Debug == true) + clog << "Re-Instated " << I.FullName(false) << " (" << OldBreaks << " vs " << Cache.BrokenCount() << ')' << endl; + } + + if (Cache[I].InstallVer == 0 || Cache[I].InstBroken() == false) + continue; + + if (Debug == true) + clog << "Investigating (" << Counter << ") " << APT::PrettyPkg(&Cache, I) << endl; + + // Isolate the problem dependency + bool InOr = false; + pkgCache::DepIterator Start; + pkgCache::DepIterator End; + size_t OldSize = 0; + + KillList.resize(0); + + enum {OrRemove,OrKeep} OrOp = OrRemove; + for (pkgCache::DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); + D.end() == false || InOr == true;) + { + // Compute a single dependency element (glob or) + if (Start == End) + { + // Decide what to do + if (InOr == true && OldSize == KillList.size()) + { + if (OrOp == OrRemove) + { + if ((Flags[I->ID] & Protected) != Protected) + { + if (Debug == true) + clog << " Or group remove for " << I.FullName(false) << endl; + Cache.MarkDelete(I, false, 0, false); + Change = true; + } + } + else if (OrOp == OrKeep) + { + if (Debug == true) + clog << " Or group keep for " << I.FullName(false) << endl; + Cache.MarkKeep(I, false, false); + Change = true; + } + } + + /* We do an extra loop (as above) to finalize the or group + processing */ + InOr = false; + OrOp = OrRemove; + D.GlobOr(Start,End); + if (Start.end() == true) + break; + + // We only worry about critical deps. + if (End.IsCritical() != true) + continue; + + InOr = Start != End; + OldSize = KillList.size(); + } + else + { + ++Start; + // We only worry about critical deps. + if (Start.IsCritical() != true) + continue; + } + + // Dep is ok + if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) + { + InOr = false; + continue; + } + + if (Debug == true) + clog << "Broken " << APT::PrettyDep(&Cache, Start) << endl; + + /* Look across the version list. If there are no possible + targets then we keep the package and bail. This is necessary + if a package has a dep on another package that can't be found */ + std::unique_ptr VList(Start.AllTargets()); + if (VList[0] == 0 && (Flags[I->ID] & Protected) != Protected && + Start.IsNegative() == false && + Cache[I].NowBroken() == false) + { + if (InOr == true) + { + /* No keep choice because the keep being OK could be the + result of another element in the OR group! */ + continue; + } + + Change = true; + Cache.MarkKeep(I, false, false); + break; + } + + bool Done = false; + for (pkgCache::Version **V = VList.get(); *V != 0; V++) + { + pkgCache::VerIterator Ver(Cache,*V); + pkgCache::PkgIterator Pkg = Ver.ParentPkg(); + + /* This is a conflicts, and the version we are looking + at is not the currently selected version of the + package, which means it is not necessary to + remove/keep */ + if (Cache[Pkg].InstallVer != Ver && Start.IsNegative() == true) + { + if (Debug) + clog << " Conflicts//Breaks against version " + << Ver.VerStr() << " for " << Pkg.Name() + << " but that is not InstVer, ignoring" + << endl; + continue; + } + + if (Debug == true) + clog << " Considering " << Pkg.FullName(false) << ' ' << Scores[Pkg->ID] << + " as a solution to " << I.FullName(false) << ' ' << Scores[I->ID] << endl; + + /* Try to fix the package under consideration rather than + fiddle with the VList package */ + if (Scores[I->ID] <= Scores[Pkg->ID] || + ((Cache[Start] & pkgDepCache::DepNow) == 0 && + End.IsNegative() == false)) + { + // Try a little harder to fix protected packages.. + if ((Flags[I->ID] & Protected) == Protected) + { + if (DoUpgrade(Pkg) == true) + { + if (Scores[Pkg->ID] > Scores[I->ID]) + Scores[Pkg->ID] = Scores[I->ID]; + break; + } + + continue; + } + + /* See if a keep will do, unless the package is protected, + then installing it will be necessary */ + bool Installed = Cache[I].Install(); + Cache.MarkKeep(I, false, false); + if (Cache[I].InstBroken() == false) + { + // Unwind operation will be keep now + if (OrOp == OrRemove) + OrOp = OrKeep; + + // Restore + if (InOr == true && Installed == true) + Cache.MarkInstall(I, false, 0, false); + + if (Debug == true) + clog << " Holding Back " << I.FullName(false) << " rather than change " << Start.TargetPkg().FullName(false) << endl; + } + else + { + if (BrokenFix == false || DoUpgrade(I) == false) + { + // Consider other options + if (InOr == false || Cache[I].Garbage == true) + { + if (Debug == true) + clog << " Removing " << I.FullName(false) << " rather than change " << Start.TargetPkg().FullName(false) << endl; + Cache.MarkDelete(I, false, 0, false); + if (Counter > 1 && Scores[Pkg->ID] > Scores[I->ID]) + Scores[I->ID] = Scores[Pkg->ID]; + } + else if (TryFixByInstall == true && + Start.TargetPkg()->CurrentVer == 0 && + Cache[Start.TargetPkg()].Delete() == false && + (Flags[Start.TargetPkg()->ID] & ToRemove) != ToRemove && + Cache.GetCandidateVersion(Start.TargetPkg()).end() == false) + { + /* Before removing or keeping the package with the broken dependency + try instead to install the first not previously installed package + solving this dependency. This helps every time a previous solver + is removed by the resolver because of a conflict or alike but it is + dangerous as it could trigger new breaks/conflicts… */ + if (Debug == true) + clog << " Try Installing " << APT::PrettyPkg(&Cache, Start.TargetPkg()) << " before changing " << I.FullName(false) << std::endl; + auto const OldBroken = Cache.BrokenCount(); + Cache.MarkInstall(Start.TargetPkg(), true, 1, false); + // FIXME: we should undo the complete MarkInstall process here + if (Cache[Start.TargetPkg()].InstBroken() == true || Cache.BrokenCount() > OldBroken) + Cache.MarkDelete(Start.TargetPkg(), false, 1, false); + } + } + } + + Change = true; + Done = true; + break; + } + else + { + if (Start->Type == pkgCache::Dep::DpkgBreaks) + { + // first, try upgradring the package, if that + // does not help, the breaks goes onto the + // kill list + // + // FIXME: use DoUpgrade(Pkg) instead? + if (Cache[End] & pkgDepCache::DepGCVer) + { + if (Debug) + clog << " Upgrading " << Pkg.FullName(false) << " due to Breaks field in " << I.FullName(false) << endl; + Cache.MarkInstall(Pkg, false, 0, false); + continue; + } + } + + // Skip adding to the kill list if it is protected + if ((Flags[Pkg->ID] & Protected) != 0) + continue; + + if (Debug == true) + clog << " Added " << Pkg.FullName(false) << " to the remove list" << endl; + + KillList.push_back({Pkg, End}); + + if (Start.IsNegative() == false) + break; + } + } + + // Hm, nothing can possibly satisfy this dep. Nuke it. + if (VList[0] == 0 && + Start.IsNegative() == false && + (Flags[I->ID] & Protected) != Protected) + { + bool Installed = Cache[I].Install(); + Cache.MarkKeep(I); + if (Cache[I].InstBroken() == false) + { + // Unwind operation will be keep now + if (OrOp == OrRemove) + OrOp = OrKeep; + + // Restore + if (InOr == true && Installed == true) + Cache.MarkInstall(I, false, 0, false); + + if (Debug == true) + clog << " Holding Back " << I.FullName(false) << " because I can't find " << Start.TargetPkg().FullName(false) << endl; + } + else + { + if (Debug == true) + clog << " Removing " << I.FullName(false) << " because I can't find " << Start.TargetPkg().FullName(false) << endl; + if (InOr == false) + Cache.MarkDelete(I, false, 0, false); + } + + Change = true; + Done = true; + } + + // Try some more + if (InOr == true) + continue; + + if (Done == true) + break; + } + + // Apply the kill list now + if (Cache[I].InstallVer != 0) + { + for (auto J = KillList.begin(); J != KillList.end(); J++) + { + Change = true; + if ((Cache[J->Dep] & pkgDepCache::DepGNow) == 0) + { + if (J->Dep.IsNegative() == true) + { + if (Debug == true) + clog << " Fixing " << I.FullName(false) << " via remove of " << J->Pkg.FullName(false) << endl; + Cache.MarkDelete(J->Pkg, false, 0, false); + } + } + else + { + if (Debug == true) + clog << " Fixing " << I.FullName(false) << " via keep of " << J->Pkg.FullName(false) << endl; + Cache.MarkKeep(J->Pkg, false, false); + } + + if (Counter > 1) + { + if (Scores[I->ID] > Scores[J->Pkg->ID]) + Scores[J->Pkg->ID] = Scores[I->ID]; + } + } + } + } + } + + if (Debug == true) + clog << "Done" << endl; + + if (Cache.BrokenCount() != 0) + { + // See if this is the result of a hold + pkgCache::PkgIterator I = Cache.PkgBegin(); + for (;I.end() != true; ++I) + { + if (Cache[I].InstBroken() == false) + continue; + if ((Flags[I->ID] & Protected) != Protected) + return _error->Error(_("Error, pkgProblemResolver::Resolve generated breaks, this may be caused by held packages.")); + } + return _error->Error(_("Unable to correct problems, you have held broken packages.")); + } + + // set the auto-flags (mvo: I'm not sure if we _really_ need this) + pkgCache::PkgIterator I = Cache.PkgBegin(); + for (;I.end() != true; ++I) { + if (Cache[I].NewInstall() && !(Flags[I->ID] & PreInstalled)) { + if(_config->FindB("Debug::pkgAutoRemove",false)) { + std::clog << "Resolve installed new pkg: " << I.FullName(false) + << " (now marking it as auto)" << std::endl; + } + Cache[I].Flags |= pkgCache::Flag::Auto; + } + } + + + return true; +} + /*}}}*/ +// ProblemResolver::BreaksInstOrPolicy - Check if the given pkg is broken/*{{{*/ +// --------------------------------------------------------------------- +/* This checks if the given package is broken either by a hard dependency + (InstBroken()) or by introducing a new policy breakage e.g. new + unsatisfied recommends for a package that was in "policy-good" state + + Note that this is not perfect as it will ignore further breakage + for already broken policy (recommends) +*/ +bool pkgProblemResolver::InstOrNewPolicyBroken(pkgCache::PkgIterator I) +{ + // a broken install is always a problem + if (Cache[I].InstBroken() == true) + { + if (Debug == true) + std::clog << " Dependencies are not satisfied for " << APT::PrettyPkg(&Cache, I) << std::endl; + return true; + } + + // a newly broken policy (recommends/suggests) is a problem + if (Cache[I].NowPolicyBroken() == false && + Cache[I].InstPolicyBroken() == true) + { + if (Debug == true) + std::clog << " Policy breaks with upgrade of " << APT::PrettyPkg(&Cache, I) << std::endl; + return true; + } + + return false; +} + /*}}}*/ +// ProblemResolver::ResolveByKeep - Resolve problems using keep /*{{{*/ +// --------------------------------------------------------------------- +/* This is the work horse of the soft upgrade routine. It is very gentle + in that it does not install or remove any packages. It is assumed that the + system was non-broken previously. */ +bool pkgProblemResolver::ResolveByKeep(OpProgress * const Progress) +{ + std::string const solver = _config->Find("APT::Solver", "internal"); + constexpr auto flags = EDSP::Request::UPGRADE_ALL | EDSP::Request::FORBID_NEW_INSTALL | EDSP::Request::FORBID_REMOVE; + auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, flags, Progress); + if (solver != "internal") + return ret; + return ResolveByKeepInternal(); +} + /*}}}*/ +// ProblemResolver::ResolveByKeepInternal - Resolve problems using keep /*{{{*/ +// --------------------------------------------------------------------- +/* This is the work horse of the soft upgrade routine. It is very gentle + in that it does not install or remove any packages. It is assumed that the + system was non-broken previously. */ +bool pkgProblemResolver::ResolveByKeepInternal() +{ + pkgDepCache::ActionGroup group(Cache); + + MakeScores(); + + /* We have to order the packages so that the broken fixing pass + operates from highest score to lowest. This prevents problems when + high score packages cause the removal of lower score packages that + would cause the removal of even lower score packages. */ + auto Size = Cache.Head().PackageCount; + pkgCache::Package **PList = new pkgCache::Package *[Size]; + pkgCache::Package **PEnd = PList; + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + *PEnd++ = I; + + std::sort(PList,PEnd,[this](Package *a, Package *b) { return ScoreSort(a, b) < 0; }); + + + if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true) + { + clog << "Show Scores" << endl; + for (pkgCache::Package **K = PList; K != PEnd; K++) + if (Scores[(*K)->ID] != 0) + { + pkgCache::PkgIterator Pkg(Cache,*K); + clog << Scores[(*K)->ID] << ' ' << APT::PrettyPkg(&Cache, Pkg) << std::endl; + } + } + + if (Debug == true) + clog << "Entering ResolveByKeep" << endl; + + // Consider each broken package + pkgCache::Package **LastStop = 0; + for (pkgCache::Package **K = PList; K != PEnd; K++) + { + pkgCache::PkgIterator I(Cache,*K); + + if (Cache[I].InstallVer == 0) + continue; + + if (InstOrNewPolicyBroken(I) == false) + continue; + + /* Keep the package. If this works then great, otherwise we have + to be significantly more aggressive and manipulate its dependencies */ + if ((Flags[I->ID] & Protected) == 0) + { + if (Debug == true) + clog << "Keeping package " << I.FullName(false) << endl; + Cache.MarkKeep(I, false, false); + if (InstOrNewPolicyBroken(I) == false) + { + K = PList - 1; + continue; + } + } + + // Isolate the problem dependencies + for (pkgCache::DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); D.end() == false;) + { + DepIterator Start; + DepIterator End; + D.GlobOr(Start,End); + + // We only worry about critical deps. + if (End.IsCritical() != true) + continue; + + // Dep is ok + if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) + continue; + + /* Hm, the group is broken.. I suppose the best thing to do is to + is to try every combination of keep/not-keep for the set, but that's + slow, and this never happens, just be conservative and assume the + list of ors is in preference and keep till it starts to work. */ + while (true) + { + if (Debug == true) + clog << "Package " << I.FullName(false) << " " << APT::PrettyDep(&Cache, Start) << endl; + + // Look at all the possible provides on this package + std::unique_ptr VList(Start.AllTargets()); + for (pkgCache::Version **V = VList.get(); *V != 0; V++) + { + pkgCache::VerIterator Ver(Cache,*V); + pkgCache::PkgIterator Pkg = Ver.ParentPkg(); + + // It is not keepable + if (Cache[Pkg].InstallVer == 0 || + Pkg->CurrentVer == 0) + continue; + + if ((Flags[I->ID] & Protected) == 0) + { + if (Debug == true) + clog << " Keeping Package " << Pkg.FullName(false) << " due to " << Start.DepType() << endl; + Cache.MarkKeep(Pkg, false, false); + } + + if (InstOrNewPolicyBroken(I) == false) + break; + } + + if (InstOrNewPolicyBroken(I) == false) + break; + + if (Start == End) + break; + ++Start; + } + + if (InstOrNewPolicyBroken(I) == false) + break; + } + + if (InstOrNewPolicyBroken(I) == true) + continue; + + // Restart again. + if (K == LastStop) { + // I is an iterator based off our temporary package list, + // so copy the name we need before deleting the temporary list + std::string const LoopingPackage = I.FullName(false); + delete[] PList; + return _error->Error("Internal Error, pkgProblemResolver::ResolveByKeep is looping on package %s.", LoopingPackage.c_str()); + } + LastStop = K; + K = PList - 1; + } + + delete[] PList; + return true; +} + /*}}}*/ +// ProblemResolver::InstallProtect - deprecated cpu-eating no-op /*{{{*/ +// --------------------------------------------------------------------- +/* Actions issued with FromUser bit set are protected from further + modification (expect by other calls with FromUser set) nowadays , so we + don't need to reissue actions here, they are already set in stone. */ +void pkgProblemResolver::InstallProtect() +{ + pkgDepCache::ActionGroup group(Cache); + + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if ((Flags[I->ID] & Protected) == Protected) + { + if ((Flags[I->ID] & ToRemove) == ToRemove) + Cache.MarkDelete(I); + else + { + // preserve the information whether the package was auto + // or manually installed + bool autoInst = (Cache[I].Flags & pkgCache::Flag::Auto); + Cache.MarkInstall(I, false, 0, !autoInst); + } + } + } +} + /*}}}*/ +// PrioSortList - Sort a list of versions by priority /*{{{*/ +// --------------------------------------------------------------------- +/* This is meant to be used in conjunction with AllTargets to get a list + of versions ordered by preference. */ + +struct PrioComp { + pkgCache &PrioCache; + + explicit PrioComp(pkgCache &PrioCache) : PrioCache(PrioCache) { + } + + bool operator() (pkgCache::Version * const &A, pkgCache::Version * const &B) { + return compare(A, B) < 0; + } + + int compare(pkgCache::Version * const &A, pkgCache::Version * const &B) { + pkgCache::VerIterator L(PrioCache,A); + pkgCache::VerIterator R(PrioCache,B); + + if ((L.ParentPkg()->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential && + (R.ParentPkg()->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential) + return 1; + if ((L.ParentPkg()->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential && + (R.ParentPkg()->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) + return -1; + + if ((L.ParentPkg()->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important && + (R.ParentPkg()->Flags & pkgCache::Flag::Important) != pkgCache::Flag::Important) + return 1; + if ((L.ParentPkg()->Flags & pkgCache::Flag::Important) != pkgCache::Flag::Important && + (R.ParentPkg()->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) + return -1; + + if (L->Priority != R->Priority) + return R->Priority - L->Priority; + return strcmp(L.ParentPkg().Name(),R.ParentPkg().Name()); + } +}; + +void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List) +{ + unsigned long Count = 0; + for (pkgCache::Version **I = List; *I != 0; I++) + Count++; + std::sort(List,List+Count,PrioComp(Cache)); +} + /*}}}*/ diff --git a/apt-private/CMakeLists.txt b/apt-private/CMakeLists.txt index 88a8f97fe..4a6ea51b8 100644 --- a/apt-private/CMakeLists.txt +++ b/apt-private/CMakeLists.txt @@ -5,14 +5,14 @@ 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 library "*.cc" "*.mm") 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) +target_link_libraries(apt-private PUBLIC apt-pkg "-framework Foundation") 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) diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc deleted file mode 100644 index eb9a34abe..000000000 --- a/apt-private/private-output.cc +++ /dev/null @@ -1,801 +0,0 @@ -// Include files /*{{{*/ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - /*}}}*/ - -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 * 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 due; - PrettyFullNameWithDue() {} - std::string operator() (pkgCache::PkgIterator const &Pkg) - { - std::string const A = PrettyFullName(Pkg); - std::map::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 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 CandidateVersion(pkgCacheFile * const Cache) -{ - return std::bind(static_cast(&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 CurrentToCandidateVersion(pkgCacheFile * const Cache) -{ - return std::bind(static_cast(&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.mm b/apt-private/private-output.mm new file mode 100644 index 000000000..2cf3dc656 --- /dev/null +++ b/apt-private/private-output.mm @@ -0,0 +1,956 @@ +// Include files /*{{{*/ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + /*}}}*/ + +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 * 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; + } + } +} + +static NSMutableDictionary * BrokenPackage(ostream &out, pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg, bool const Now) +{ + if (Now == true) + { + if ((*Cache)[Pkg].NowBroken() == false) + return nil; + } + else + { + if ((*Cache)[Pkg].InstBroken() == false) + return nil; + } + + NSMutableDictionary *packages = [NSMutableDictionary dictionary]; + + // Array of depends for package + NSMutableArray *deps = [NSMutableArray array]; + + // Print out each package and the failed dependencies + packages[@(Pkg.FullName(true).c_str())] = deps; + + pkgCache::VerIterator Ver; + + if (Now == true) + Ver = Pkg.CurrentVer(); + else + Ver = (*Cache)[Pkg].InstVerIter(*Cache); + + if (Ver.end() == true) + { + return packages; + } + + 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; + } + + // Array of depends that can satisfy this depend + NSMutableArray *possibleDepends = [NSMutableArray array]; + [deps addObject:possibleDepends]; + + while (1) + { + + NSMutableDictionary *dependency = [NSMutableDictionary dictionary]; + dependency[@"Type"] = @(End.DepType()); + dependency[@"Package"] = @(Start.TargetPkg().FullName(true).c_str()); + + // Show a quick summary of the version requirements + if (Start.TargetVer() != 0) { + dependency[@"VersionSummary"] = [NSString stringWithFormat:@"%s %s", Start.CompType(), Start.TargetVer()]; + dependency[@"CompType"] = @(Start.CompType()); + dependency[@"TargetVer"] = @(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) + { + pkgCache::VerIterator Ver = (*Cache)[Targ].InstVerIter(*Cache); + if (Now == true) + Ver = Targ.CurrentVer(); + + if (Ver.end() == false) + { + dependency[@"VerStr"] = @(Ver.VerStr()); + if (Now == true) + dependency[@"Reason"] = [NSString stringWithFormat:@"%s is installed",Ver.VerStr()]; + else + dependency[@"Reason"] = [NSString stringWithFormat:@"%s is to be installed",Ver.VerStr()]; + } + else + { + if ((*Cache)[Targ].CandidateVerIter(*Cache).end() == true) + { + if (Targ->ProvidesList == 0) + dependency[@"Reason"] = @"it is not installable"; + else + dependency[@"Reason"] = @"it is a virtual package"; + } + else { + if (Now) + dependency[@"Reason"] = @"it is not installed"; + else + dependency[@"Reason"] = @"it is not going to be installed"; + } + } + } + + [possibleDepends addObject:dependency]; + if (Start == End) + break; + ++Start; + } + } + return packages; +} + +void ShowBroken(ostream &out, CacheFile &Cache, bool const Now) +{ + if (Cache->BrokenCount() == 0) + return; + + out << _("The following packages have unmet dependencies:") << endl; + + bool useJSON = _config->FindB("APT::Format::JSON", false); + + SortedPackageUniverse Universe(Cache); + + if (useJSON){ + NSMutableDictionary *packages = [NSMutableDictionary dictionary]; + for (auto const &Pkg: Universe){ + NSMutableDictionary *package = BrokenPackage(out, &Cache, Pkg, Now); + [packages addEntriesFromDictionary:package]; + } + + NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"brokenPackages": packages} options:NSJSONWritingPrettyPrinted error:nil]; + NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput]; + [stdout writeData:data]; + + cout << endl << flush; + } else { + 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; + + bool useJSON = _config->FindB("APT::Format::JSON", false); + + APT::PackageUniverse Universe(Cache); + + if (useJSON){ + NSMutableDictionary *packages = [NSMutableDictionary dictionary]; + for (auto const &Pkg: Universe){ + NSMutableDictionary *package = BrokenPackage(out, &Cache, Pkg, Now); + [packages addEntriesFromDictionary:package]; + } + + NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"brokenPackages": packages} options:NSJSONWritingPrettyPrinted error:nil]; + NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput]; + [stdout writeData:data]; + + cout << endl << flush; + } else { + 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 due; + PrettyFullNameWithDue() {} + std::string operator() (pkgCache::PkgIterator const &Pkg) + { + std::string const A = PrettyFullName(Pkg); + std::map::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 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 CandidateVersion(pkgCacheFile * const Cache) +{ + return std::bind(static_cast(&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 CurrentToCandidateVersion(pkgCacheFile * const Cache) +{ + return std::bind(static_cast(&CurrentToCandidateVersion), Cache, std::placeholders::_1); +} +bool AlwaysTrue(pkgCache::PkgIterator const &) +{ + return true; +} +std::string EmptyString(pkgCache::PkgIterator const &) +{ + return std::string(); +} + -- cgit v1.2.3