summaryrefslogtreecommitdiffstats
path: root/apt-pkg/pkgcache.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 09:59:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 09:59:37 +0000
commit76e2632459410dec81337edb6a9fee33c9a660f3 (patch)
treea73345df208eede4a4daad340515c9328f34625c /apt-pkg/pkgcache.cc
parentInitial commit. (diff)
downloadapt-76e2632459410dec81337edb6a9fee33c9a660f3.tar.xz
apt-76e2632459410dec81337edb6a9fee33c9a660f3.zip
Adding upstream version 2.7.12.upstream/2.7.12
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'apt-pkg/pkgcache.cc')
-rw-r--r--apt-pkg/pkgcache.cc1046
1 files changed, 1046 insertions, 0 deletions
diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc
new file mode 100644
index 0000000..ffe30fa
--- /dev/null
+++ b/apt-pkg/pkgcache.cc
@@ -0,0 +1,1046 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Package Cache - Accessor code for the cache
+
+ Please see doc/apt-pkg/cache.sgml for a more detailed description of
+ this format. Also be sure to keep that file up-to-date!!
+
+ This is the general utility functions for cache management. They provide
+ a complete set of accessor functions for the cache. The cacheiterators
+ header contains the STL-like iterators that can be used to easially
+ navigate the cache as well as seamlessly dereference the mmap'd
+ indexes. Use these always.
+
+ The main class provides for ways to get package indexes and some
+ general lookup functions to start the iterators.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/mmap.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/version.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <cstring>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <sys/stat.h>
+#include <xxhash.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using std::string;
+using APT::StringView;
+
+
+// Cache::Header::Header - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* Simply initialize the header */
+pkgCache::Header::Header()
+{
+#define APT_HEADER_SET(X,Y) X = Y; static_assert(std::numeric_limits<decltype(X)>::max() > Y, "Size violation detected in pkgCache::Header")
+ APT_HEADER_SET(Signature, 0x98FE76DC);
+
+ /* Whenever the structures change the major version should be bumped,
+ whenever the generator changes the minor version should be bumped. */
+ APT_HEADER_SET(MajorVersion, 16);
+ APT_HEADER_SET(MinorVersion, 0);
+ APT_HEADER_SET(Dirty, false);
+
+ APT_HEADER_SET(HeaderSz, sizeof(pkgCache::Header));
+ APT_HEADER_SET(GroupSz, sizeof(pkgCache::Group));
+ APT_HEADER_SET(PackageSz, sizeof(pkgCache::Package));
+ APT_HEADER_SET(ReleaseFileSz, sizeof(pkgCache::ReleaseFile));
+ APT_HEADER_SET(PackageFileSz, sizeof(pkgCache::PackageFile));
+ APT_HEADER_SET(VersionSz, sizeof(pkgCache::Version));
+ APT_HEADER_SET(DescriptionSz, sizeof(pkgCache::Description));
+ APT_HEADER_SET(DependencySz, sizeof(pkgCache::Dependency));
+ APT_HEADER_SET(DependencyDataSz, sizeof(pkgCache::DependencyData));
+ APT_HEADER_SET(ProvidesSz, sizeof(pkgCache::Provides));
+ APT_HEADER_SET(VerFileSz, sizeof(pkgCache::VerFile));
+ APT_HEADER_SET(DescFileSz, sizeof(pkgCache::DescFile));
+#undef APT_HEADER_SET
+
+ GroupCount = 0;
+ PackageCount = 0;
+ VersionCount = 0;
+ DescriptionCount = 0;
+ DependsCount = 0;
+ DependsDataCount = 0;
+ ReleaseFileCount = 0;
+ PackageFileCount = 0;
+ VerFileCount = 0;
+ DescFileCount = 0;
+ ProvidesCount = 0;
+ MaxVerFileSize = 0;
+ MaxDescFileSize = 0;
+
+ FileList = 0;
+ RlsFileList = 0;
+ VerSysName = 0;
+ Architecture = 0;
+ SetArchitectures(0);
+ SetHashTableSize(_config->FindI("APT::Cache-HashTableSize", 196613));
+ memset(Pools,0,sizeof(Pools));
+
+ CacheFileSize = 0;
+}
+ /*}}}*/
+// Cache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgCache::Header::CheckSizes(Header &Against) const
+{
+ if (HeaderSz == Against.HeaderSz &&
+ GroupSz == Against.GroupSz &&
+ PackageSz == Against.PackageSz &&
+ ReleaseFileSz == Against.ReleaseFileSz &&
+ PackageFileSz == Against.PackageFileSz &&
+ VersionSz == Against.VersionSz &&
+ DescriptionSz == Against.DescriptionSz &&
+ DependencySz == Against.DependencySz &&
+ DependencyDataSz == Against.DependencyDataSz &&
+ VerFileSz == Against.VerFileSz &&
+ DescFileSz == Against.DescFileSz &&
+ ProvidesSz == Against.ProvidesSz)
+ return true;
+ return false;
+}
+ /*}}}*/
+
+// Cache::pkgCache - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+pkgCache::pkgCache(MMap *Map, bool DoMap) : Map(*Map), VS(nullptr), d(NULL)
+{
+ // call getArchitectures() with cached=false to ensure that the
+ // architectures cache is re-evaluated. this is needed in cases
+ // when the APT::Architecture field changes between two cache creations
+ APT::Configuration::getArchitectures(false);
+ MultiArchEnabled = true;
+ if (DoMap == true)
+ ReMap();
+}
+ /*}}}*/
+// Cache::ReMap - Reopen the cache file /*{{{*/
+// ---------------------------------------------------------------------
+/* If the file is already closed then this will open it open it. */
+bool pkgCache::ReMap(bool const &Errorchecks)
+{
+ // Apply the typecasts.
+ HeaderP = (Header *)Map.Data();
+ GrpP = (Group *)Map.Data();
+ PkgP = (Package *)Map.Data();
+ VerFileP = (VerFile *)Map.Data();
+ DescFileP = (DescFile *)Map.Data();
+ RlsFileP = (ReleaseFile *)Map.Data();
+ PkgFileP = (PackageFile *)Map.Data();
+ VerP = (Version *)Map.Data();
+ DescP = (Description *)Map.Data();
+ ProvideP = (Provides *)Map.Data();
+ DepP = (Dependency *)Map.Data();
+ DepDataP = (DependencyData *)Map.Data();
+ StrP = (char *)Map.Data();
+
+ if (Errorchecks == false)
+ return true;
+
+ if (Map.Size() == 0 || HeaderP == 0)
+ return _error->Error(_("Empty package cache"));
+
+ // Check the header
+ Header DefHeader;
+ if (HeaderP->Signature != DefHeader.Signature ||
+ HeaderP->Dirty == true)
+ return _error->Error(_("The package cache file is corrupted"));
+
+ if (HeaderP->MajorVersion != DefHeader.MajorVersion ||
+ HeaderP->MinorVersion != DefHeader.MinorVersion ||
+ HeaderP->CheckSizes(DefHeader) == false)
+ return _error->Error(_("The package cache file is an incompatible version"));
+
+ if (HeaderP->VerSysName == 0 || HeaderP->Architecture == 0 || HeaderP->GetArchitectures() == 0)
+ return _error->Error(_("The package cache file is corrupted"));
+
+ // Locate our VS..
+ if ((VS = pkgVersioningSystem::GetVS(StrP + HeaderP->VerSysName)) == 0)
+ return _error->Error(_("This APT does not support the versioning system '%s'"),StrP + HeaderP->VerSysName);
+
+ // Check the architecture
+ std::vector<std::string> archs = APT::Configuration::getArchitectures();
+ std::string list = "";
+ for (auto const & arch : archs) {
+ if (!list.empty())
+ list.append(",");
+ list.append(arch);
+ }
+ if (_config->Find("APT::Architecture") != StrP + HeaderP->Architecture ||
+ list != StrP + HeaderP->GetArchitectures())
+ return _error->Error(_("The package cache was built for different architectures: %s vs %s"), StrP + HeaderP->GetArchitectures(), list.c_str());
+
+
+ auto hash = CacheHash();
+ if (_config->FindB("Debug::pkgCacheGen", false))
+ std::clog << "Opened cache with hash " << hash << ", expecting " << HeaderP->CacheFileSize << "\n";
+ if (hash != HeaderP->CacheFileSize)
+ return _error->Error(_("The package cache file is corrupted, it has the wrong hash"));
+
+ return true;
+}
+ /*}}}*/
+// Cache::Hash - Hash a string /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to generate the hash entries for the HashTable. With my
+ package list from bo this function gets 94% table usage on a 512 item
+ table (480 used items) */
+map_id_t pkgCache::sHash(StringView Str) const
+{
+ uint32_t Hash = 5381;
+ auto I = Str.begin();
+ auto End = Str.end();
+ for (; I + 7 < End; I += 8)
+ {
+ Hash = (33u * 33u * 33u * 33u * 33u * 33u * 33u * 33u * Hash +
+ 33u * 33u * 33u * 33u * 33u * 33u * 33u * tolower_ascii_unsafe(I[0]) +
+ 33u * 33u * 33u * 33u * 33u * 33u * tolower_ascii_unsafe(I[1]) +
+ 33u * 33u * 33u * 33u * 33u * tolower_ascii_unsafe(I[2]) +
+ 33u * 33u * 33u * 33u * tolower_ascii_unsafe(I[3]) +
+ 33u * 33u * 33u * tolower_ascii_unsafe(I[4]) +
+ 33u * 33u * tolower_ascii_unsafe(I[5]) +
+ 33u * tolower_ascii_unsafe(I[6]) +
+ tolower_ascii_unsafe(I[7]));
+ }
+ for (; I != End; ++I)
+ Hash = 33u * Hash + tolower_ascii_unsafe(*I);
+ return Hash % HeaderP->GetHashTableSize();
+}
+uint32_t pkgCache::CacheHash()
+{
+ pkgCache::Header header = {};
+ XXH3_state_t *state = XXH3_createState();
+
+ if (Map.Size() < sizeof(header))
+ return 0;
+
+ XXH3_64bits_reset(state);
+ memcpy(&header, GetMap().Data(), sizeof(header));
+
+ header.Dirty = false;
+ header.CacheFileSize = 0;
+
+ XXH3_64bits_update(state,
+ reinterpret_cast<const unsigned char *>(PACKAGE_VERSION),
+ APT_ARRAY_SIZE(PACKAGE_VERSION));
+
+ XXH3_64bits_update(state,
+ reinterpret_cast<const unsigned char *>(&header),
+ sizeof(header));
+
+ if (Map.Size() > sizeof(header)) {
+ XXH3_64bits_update(state,
+ static_cast<const unsigned char *>(GetMap().Data()) + sizeof(header),
+ GetMap().Size() - sizeof(header));
+ }
+
+ auto const digest = XXH3_64bits_digest(state);
+ XXH3_freeState(state);
+ return digest & 0xFFFFFFFF;
+}
+ /*}}}*/
+// Cache::FindPkg - Locate a package by name /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns 0 on error, pointer to the package otherwise */
+pkgCache::PkgIterator pkgCache::FindPkg(StringView Name) {
+ auto const found = Name.rfind(':');
+ if (found == string::npos)
+ return FindPkg(Name, "native");
+ auto const Arch = Name.substr(found+1);
+ /* Beware: This is specialcased to handle pkg:any in dependencies
+ as these are linked to virtual pkg:any named packages.
+ If you want any arch from a pkg, use FindPkg(pkg,"any") */
+ if (Arch == "any")
+ return FindPkg(Name, "any");
+ return FindPkg(Name.substr(0, found), Arch);
+}
+ /*}}}*/
+// Cache::FindPkg - Locate a package by name /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns 0 on error, pointer to the package otherwise */
+pkgCache::PkgIterator pkgCache::FindPkg(StringView Name, StringView Arch) {
+ /* We make a detour via the GrpIterator here as
+ on a multi-arch environment a group is easier to
+ find than a package (less entries in the buckets) */
+ pkgCache::GrpIterator Grp = FindGrp(Name);
+ if (Grp.end() == true)
+ return PkgIterator(*this,0);
+
+ return Grp.FindPkg(Arch);
+}
+ /*}}}*/
+// Cache::FindGrp - Locate a group by name /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns End-Pointer on error, pointer to the group otherwise */
+pkgCache::GrpIterator pkgCache::FindGrp(StringView Name) {
+ if (unlikely(Name.empty() == true))
+ return GrpIterator(*this,0);
+
+ // Look at the hash bucket for the group
+ Group *Grp = GrpP + HeaderP->GrpHashTableP()[sHash(Name)];
+ for (; Grp != GrpP; Grp = GrpP + Grp->Next) {
+ int const cmp = StringViewCompareFast(Name, ViewString(Grp->Name));
+ if (cmp == 0)
+ return GrpIterator(*this, Grp);
+ else if (cmp < 0)
+ break;
+ }
+
+ return GrpIterator(*this,0);
+}
+ /*}}}*/
+// Cache::CompTypeDeb - Return a string describing the compare type /*{{{*/
+// ---------------------------------------------------------------------
+/* This returns a string representation of the dependency compare
+ type in the weird debian style.. */
+const char *pkgCache::CompTypeDeb(unsigned char Comp)
+{
+ const char * const Ops[] = {"","<=",">=","<<",">>","=","!="};
+ if (unlikely((unsigned)(Comp & 0xF) >= sizeof(Ops)/sizeof(Ops[0])))
+ return "";
+ return Ops[Comp & 0xF];
+}
+ /*}}}*/
+// Cache::CompType - Return a string describing the compare type /*{{{*/
+// ---------------------------------------------------------------------
+/* This returns a string representation of the dependency compare
+ type */
+const char *pkgCache::CompType(unsigned char Comp)
+{
+ const char * const Ops[] = {"","<=",">=","<",">","=","!="};
+ if (unlikely((unsigned)(Comp & 0xF) >= sizeof(Ops)/sizeof(Ops[0])))
+ return "";
+ return Ops[Comp & 0xF];
+}
+ /*}}}*/
+// Cache::DepType - Return a string describing the dep type /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+const char *pkgCache::DepType(unsigned char Type)
+{
+ const char *Types[] = {"",_("Depends"),_("PreDepends"),_("Suggests"),
+ _("Recommends"),_("Conflicts"),_("Replaces"),
+ _("Obsoletes"),_("Breaks"), _("Enhances")};
+ if (Type < sizeof(Types)/sizeof(*Types))
+ return Types[Type];
+ return "";
+}
+ /*}}}*/
+// Cache::Priority - Convert a priority value to a string /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+const char *pkgCache::Priority(unsigned char Prio)
+{
+ const char *Mapping[] = {0,_("required"),_("important"),_("standard"),
+ _("optional"),_("extra")};
+ if (Prio < APT_ARRAY_SIZE(Mapping))
+ return Mapping[Prio];
+ return 0;
+}
+ /*}}}*/
+// GrpIterator::FindPkg - Locate a package by arch /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns an End-Pointer on error, pointer to the package otherwise */
+pkgCache::PkgIterator pkgCache::GrpIterator::FindPkg(StringView Arch) const {
+ if (unlikely(IsGood() == false || S->FirstPackage == 0))
+ return PkgIterator(*Owner, 0);
+
+ /* If we accept any package we simply return the "first"
+ package in this group */
+ if (Arch == "any")
+ return PkgIterator(*Owner, Owner->PkgP + S->FirstPackage);
+ if (Arch == "native" || Arch == "all")
+ Arch = Owner->NativeArch();
+
+ // Iterate over the list to find the matching arch
+ for (pkgCache::Package *Pkg = PackageList(); Pkg != Owner->PkgP;
+ Pkg = Owner->PkgP + Pkg->NextPackage) {
+ if (Arch == Owner->ViewString(Pkg->Arch))
+ return PkgIterator(*Owner, Pkg);
+ if ((Owner->PkgP + S->LastPackage) == Pkg)
+ break;
+ }
+
+ return PkgIterator(*Owner, 0);
+}
+ /*}}}*/
+// GrpIterator::FindPreferredPkg - Locate the "best" package /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns an End-Pointer on error, pointer to the package otherwise */
+pkgCache::PkgIterator pkgCache::GrpIterator::FindPreferredPkg(bool const &PreferNonVirtual) const {
+ pkgCache::PkgIterator Pkg = FindPkg(StringView("native", 6));
+ if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
+ return Pkg;
+ // native and foreign
+ for (auto const &a : APT::Configuration::getArchitectures())
+ {
+ Pkg = FindPkg(a);
+ if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
+ return Pkg;
+ }
+ // very foreign/barbarian
+ for (auto const &a : _config->FindVector("APT::BarbarianArchitectures"))
+ {
+ Pkg = FindPkg(a);
+ if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
+ return Pkg;
+ }
+ // packages without an architecture
+ Pkg = FindPkg(StringView("none", 4));
+ if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
+ return Pkg;
+ // the "rest" we somehow know about (+ those we tried already again as skipping is hard)
+ for (Pkg = PackageList(); not Pkg.end(); Pkg = NextPkg(Pkg))
+ if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
+ return Pkg;
+
+ if (PreferNonVirtual == true)
+ return FindPreferredPkg(false);
+ return PkgIterator(*Owner, 0);
+}
+ /*}}}*/
+// GrpIterator::NextPkg - Locate the next package in the group /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns an End-Pointer on error, pointer to the package otherwise.
+ We can't simply ++ to the next as the next package of the last will
+ be from a different group (with the same hash value) */
+pkgCache::PkgIterator pkgCache::GrpIterator::NextPkg(pkgCache::PkgIterator const &LastPkg) const {
+ if (unlikely(IsGood() == false || S->FirstPackage == 0 ||
+ LastPkg.end() == true))
+ return PkgIterator(*Owner, 0);
+
+ if (S->LastPackage == LastPkg.MapPointer())
+ return PkgIterator(*Owner, 0);
+
+ return PkgIterator(*Owner, Owner->PkgP + LastPkg->NextPackage);
+}
+ /*}}}*/
+// GrpIterator::operator++ - Prefix incr /*{{{*/
+// ---------------------------------------------------------------------
+/* This will advance to the next logical group in the hash table. */
+pkgCache::GrpIterator& pkgCache::GrpIterator::operator++()
+{
+ // Follow the current links
+ if (S != Owner->GrpP)
+ S = Owner->GrpP + S->Next;
+
+ // Follow the hash table
+ while (S == Owner->GrpP && (HashIndex+1) < (signed)Owner->HeaderP->GetHashTableSize())
+ {
+ ++HashIndex;
+ S = Owner->GrpP + Owner->HeaderP->GrpHashTableP()[HashIndex];
+ }
+ return *this;
+}
+ /*}}}*/
+// PkgIterator::operator++ - Prefix incr /*{{{*/
+// ---------------------------------------------------------------------
+/* This will advance to the next logical package in the hash table. */
+pkgCache::PkgIterator& pkgCache::PkgIterator::operator++()
+{
+ // Follow the current links
+ if (S != Owner->PkgP)
+ S = Owner->PkgP + S->NextPackage;
+
+ // Follow the hash table
+ while (S == Owner->PkgP && (HashIndex+1) < (signed)Owner->HeaderP->GetHashTableSize())
+ {
+ ++HashIndex;
+ S = Owner->PkgP + Owner->HeaderP->PkgHashTableP()[HashIndex];
+ }
+ return *this;
+}
+ /*}}}*/
+pkgCache::DepIterator& pkgCache::DepIterator::operator++() /*{{{*/
+{
+ if (S == Owner->DepP)
+ return *this;
+ S = Owner->DepP + (Type == DepVer ? S->NextDepends : S->NextRevDepends);
+ if (S == Owner->DepP)
+ S2 = Owner->DepDataP;
+ else
+ S2 = Owner->DepDataP + S->DependencyData;
+ return *this;
+}
+ /*}}}*/
+// PkgIterator::State - Check the State of the package /*{{{*/
+// ---------------------------------------------------------------------
+/* By this we mean if it is either cleanly installed or cleanly removed. */
+pkgCache::PkgIterator::OkState pkgCache::PkgIterator::State() const
+{
+ if (S->InstState == pkgCache::State::ReInstReq ||
+ S->InstState == pkgCache::State::HoldReInstReq)
+ return NeedsUnpack;
+
+ if (S->CurrentState == pkgCache::State::UnPacked ||
+ S->CurrentState == pkgCache::State::HalfConfigured)
+ // we leave triggers alone completely. dpkg deals with
+ // them in a hard-to-predict manner and if they get
+ // resolved by dpkg before apt run dpkg --configure on
+ // the TriggersPending package dpkg returns a error
+ //Pkg->CurrentState == pkgCache::State::TriggersAwaited
+ //Pkg->CurrentState == pkgCache::State::TriggersPending)
+ return NeedsConfigure;
+
+ if (S->CurrentState == pkgCache::State::HalfInstalled ||
+ S->InstState != pkgCache::State::Ok)
+ return NeedsUnpack;
+
+ return NeedsNothing;
+}
+ /*}}}*/
+// PkgIterator::CurVersion - Returns the current version string /*{{{*/
+// ---------------------------------------------------------------------
+/* Return string representing of the current version. */
+const char *
+pkgCache::PkgIterator::CurVersion() const
+{
+ VerIterator version = CurrentVer();
+ if (version.IsGood())
+ return CurrentVer().VerStr();
+ return 0;
+}
+ /*}}}*/
+// ostream operator to handle string representation of a package /*{{{*/
+// ---------------------------------------------------------------------
+/* Output name < cur.rent.version -> candid.ate.version | new.est.version > (section)
+ Note that the characters <|>() are all literal above. Versions will be omitted
+ if they provide no new information (e.g. there is no newer version than candidate)
+ If no version and/or section can be found "none" is used. */
+std::ostream&
+operator<<(std::ostream& out, pkgCache::PkgIterator Pkg)
+{
+ if (Pkg.end() == true)
+ return out << "invalid package";
+
+ string current = string(Pkg.CurVersion() == 0 ? "none" : Pkg.CurVersion());
+ string newest = string(Pkg.VersionList().end() ? "none" : Pkg.VersionList().VerStr());
+
+ out << Pkg.Name() << " [ " << Pkg.Arch() << " ] < " << current;
+ if ( newest != "none")
+ out << " | " << newest;
+ if (Pkg->VersionList == 0)
+ out << " > ( none )";
+ else
+ out << " > ( " << string(Pkg.VersionList().Section()==0?"unknown":Pkg.VersionList().Section()) << " )";
+ return out;
+}
+ /*}}}*/
+// PkgIterator::FullName - Returns Name and (maybe) Architecture /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns a name:arch string */
+std::string pkgCache::PkgIterator::FullName(bool const &Pretty) const
+{
+ string fullname = Name();
+ if (Pretty == false ||
+ (strcmp(Arch(), "all") != 0 && strcmp(Arch(), "any") != 0 &&
+ strcmp(Owner->NativeArch(), Arch()) != 0))
+ return fullname.append(":").append(Arch());
+ return fullname;
+}
+ /*}}}*/
+// DepIterator::IsCritical - Returns true if the dep is important /*{{{*/
+// ---------------------------------------------------------------------
+/* Currently critical deps are defined as depends, predepends and
+ conflicts (including dpkg's Breaks fields). */
+bool pkgCache::DepIterator::IsCritical() const
+{
+ if (IsNegative() == true ||
+ S2->Type == pkgCache::Dep::Depends ||
+ S2->Type == pkgCache::Dep::PreDepends)
+ return true;
+ return false;
+}
+ /*}}}*/
+// DepIterator::IsNegative - Returns true if the dep is a negative one /*{{{*/
+// ---------------------------------------------------------------------
+/* Some dependencies are positive like Depends and Recommends, others
+ are negative like Conflicts which can and should be handled differently */
+bool pkgCache::DepIterator::IsNegative() const
+{
+ return S2->Type == Dep::DpkgBreaks ||
+ S2->Type == Dep::Conflicts ||
+ S2->Type == Dep::Obsoletes;
+}
+ /*}}}*/
+// DepIterator::SmartTargetPkg - Resolve dep target pointers w/provides /*{{{*/
+// ---------------------------------------------------------------------
+/* This intellegently looks at dep target packages and tries to figure
+ out which package should be used. This is needed to nicely handle
+ provide mapping. If the target package has no other providing packages
+ then it returned. Otherwise the providing list is looked at to
+ see if there is one unique providing package if so it is returned.
+ Otherwise true is returned and the target package is set. The return
+ result indicates whether the node should be expandable
+
+ In Conjunction with the DepCache the value of Result may not be
+ super-good since the policy may have made it uninstallable. Using
+ AllTargets is better in this case. */
+bool pkgCache::DepIterator::SmartTargetPkg(PkgIterator &Result) const
+{
+ Result = TargetPkg();
+
+ // No provides at all
+ if (Result->ProvidesList == 0)
+ return false;
+
+ // There is the Base package and the providing ones which is at least 2
+ if (Result->VersionList != 0)
+ return true;
+
+ /* We have to skip over indirect provisions of the package that
+ owns the dependency. For instance, if libc5-dev depends on the
+ virtual package libc-dev which is provided by libc5-dev and libc6-dev
+ we must ignore libc5-dev when considering the provides list. */
+ PrvIterator PStart = Result.ProvidesList();
+ for (; PStart.end() != true && PStart.OwnerPkg() == ParentPkg(); ++PStart);
+
+ // Nothing but indirect self provides
+ if (PStart.end() == true)
+ return false;
+
+ // Check for single packages in the provides list
+ PrvIterator P = PStart;
+ for (; P.end() != true; ++P)
+ {
+ // Skip over self provides
+ if (P.OwnerPkg() == ParentPkg())
+ continue;
+ if (PStart.OwnerPkg() != P.OwnerPkg())
+ break;
+ }
+
+ Result = PStart.OwnerPkg();
+
+ // Check for non dups
+ if (P.end() != true)
+ return true;
+
+ return false;
+}
+ /*}}}*/
+// DepIterator::AllTargets - Returns the set of all possible targets /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a more useful version of TargetPkg() that follows versioned
+ provides. It includes every possible package-version that could satisfy
+ the dependency. The last item in the list has a 0. The resulting pointer
+ must be delete [] 'd */
+pkgCache::Version **pkgCache::DepIterator::AllTargets() const
+{
+ Version **Res = 0;
+ unsigned long Size =0;
+ while (1)
+ {
+ Version **End = Res;
+ PkgIterator DPkg = TargetPkg();
+
+ // Walk along the actual package providing versions
+ for (VerIterator I = DPkg.VersionList(); I.end() == false; ++I)
+ {
+ if (IsIgnorable(I.ParentPkg()) == true)
+ continue;
+ if (IsSatisfied(I) == false)
+ continue;
+
+ Size++;
+ if (Res != 0)
+ *End++ = I;
+ }
+
+ // Follow all provides
+ for (PrvIterator I = DPkg.ProvidesList(); I.end() == false; ++I)
+ {
+ if (IsIgnorable(I) == true)
+ continue;
+ if (IsSatisfied(I) == false)
+ continue;
+
+ Size++;
+ if (Res != 0)
+ *End++ = I.OwnerVer();
+ }
+
+ // Do it again and write it into the array
+ if (Res == 0)
+ {
+ Res = new Version *[Size+1];
+ Size = 0;
+ }
+ else
+ {
+ *End = 0;
+ break;
+ }
+ }
+
+ return Res;
+}
+ /*}}}*/
+// DepIterator::GlobOr - Compute an OR group /*{{{*/
+// ---------------------------------------------------------------------
+/* This Takes an iterator, iterates past the current dependency grouping
+ and returns Start and End so that so End is the final element
+ in the group, if End == Start then D is End++ and End is the
+ dependency D was pointing to. Use in loops to iterate sensibly. */
+void pkgCache::DepIterator::GlobOr(DepIterator &Start,DepIterator &End)
+{
+ // Compute a single dependency element (glob or)
+ Start = *this;
+ End = *this;
+ for (bool LastOR = true; end() == false && LastOR == true;)
+ {
+ LastOR = (S2->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or;
+ ++(*this);
+ if (LastOR == true)
+ End = (*this);
+ }
+}
+ /*}}}*/
+// DepIterator::IsIgnorable - should this packag/providr be ignored? /*{{{*/
+// ---------------------------------------------------------------------
+/* Deps like self-conflicts should be ignored as well as implicit conflicts
+ on virtual packages. */
+bool pkgCache::DepIterator::IsIgnorable(PkgIterator const &PT) const
+{
+ if (IsNegative() == false)
+ return false;
+
+ pkgCache::PkgIterator const PP = ParentPkg();
+ if (PP->Group != PT->Group)
+ return false;
+ // self-conflict
+ if (PP == PT)
+ return true;
+ pkgCache::VerIterator const PV = ParentVer();
+ // ignore group-conflict on a M-A:same package - but not our implicit dependencies
+ // so that we can have M-A:same packages conflicting with their own real name
+ if ((PV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
+ return IsMultiArchImplicit() == false;
+
+ return false;
+}
+bool pkgCache::DepIterator::IsIgnorable(PrvIterator const &Prv) const
+{
+ if (IsNegative() == false)
+ return false;
+
+ PkgIterator const Pkg = ParentPkg();
+ /* Provides may never be applied against the same package (or group)
+ if it is a conflicts. See the comment above. */
+ if (Prv.OwnerPkg()->Group == Pkg->Group)
+ return true;
+ // Implicit group-conflicts should not be applied on providers of other groups
+ if (IsMultiArchImplicit() && Prv.OwnerPkg()->Group != Pkg->Group)
+ return true;
+
+ return false;
+}
+ /*}}}*/
+// DepIterator::IsSatisfied - check if a version satisfied the dependency /*{{{*/
+bool pkgCache::DepIterator::IsSatisfied(VerIterator const &Ver) const
+{
+ return Owner->VS->CheckDep(Ver.VerStr(),S2->CompareOp,TargetVer());
+}
+bool pkgCache::DepIterator::IsSatisfied(PrvIterator const &Prv) const
+{
+ return Owner->VS->CheckDep(Prv.ProvideVersion(),S2->CompareOp,TargetVer());
+}
+ /*}}}*/
+// DepIterator::IsImplicit - added by the cache generation /*{{{*/
+bool pkgCache::DepIterator::IsImplicit() const
+{
+ if (IsMultiArchImplicit() == true)
+ return true;
+ if (IsNegative() || S2->Type == pkgCache::Dep::Replaces)
+ {
+ if ((S2->CompareOp & pkgCache::Dep::ArchSpecific) != pkgCache::Dep::ArchSpecific &&
+ strcmp(ParentPkg().Arch(), TargetPkg().Arch()) != 0)
+ return true;
+ }
+ return false;
+}
+ /*}}}*/
+// ostream operator to handle string representation of a dependency /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+std::ostream& operator<<(std::ostream& out, pkgCache::DepIterator D)
+{
+ if (D.end() == true)
+ return out << "invalid dependency";
+
+ pkgCache::PkgIterator P = D.ParentPkg();
+ pkgCache::PkgIterator T = D.TargetPkg();
+
+ out << (P.end() ? "invalid pkg" : P.FullName(false)) << " " << D.DepType()
+ << " on ";
+APT_IGNORE_DEPRECATED_PUSH
+ if (T.end() == true)
+ out << "invalid pkg";
+ else
+ out << T;
+APT_IGNORE_DEPRECATED_POP
+
+ if (D->Version != 0)
+ out << " (" << D.CompType() << " " << D.TargetVer() << ")";
+
+ return out;
+}
+ /*}}}*/
+// VerIterator::CompareVer - Fast version compare for same pkgs /*{{{*/
+// ---------------------------------------------------------------------
+/* This just looks over the version list to see if B is listed before A. In
+ most cases this will return in under 4 checks, ver lists are short. */
+int pkgCache::VerIterator::CompareVer(const VerIterator &B) const
+{
+ // Check if they are equal
+ if (*this == B)
+ return 0;
+ if (end() == true)
+ return -1;
+ if (B.end() == true)
+ return 1;
+
+ /* Start at A and look for B. If B is found then A > B otherwise
+ B was before A so A < B */
+ VerIterator I = *this;
+ for (;I.end() == false; ++I)
+ if (I == B)
+ return 1;
+ return -1;
+}
+ /*}}}*/
+// VerIterator::Downloadable - Checks if the version is downloadable /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+APT_PURE bool pkgCache::VerIterator::Downloadable() const
+{
+ VerFileIterator Files = FileList();
+ for (; Files.end() == false; ++Files)
+ if (Files.File().Flagged(pkgCache::Flag::NotSource) == false)
+ return true;
+ return false;
+}
+ /*}}}*/
+// VerIterator::Automatic - Check if this version is 'automatic' /*{{{*/
+// ---------------------------------------------------------------------
+/* This checks to see if any of the versions files are not NotAutomatic.
+ True if this version is selectable for automatic installation. */
+APT_PURE bool pkgCache::VerIterator::Automatic() const
+{
+ VerFileIterator Files = FileList();
+ for (; Files.end() == false; ++Files)
+ // Do not check ButAutomaticUpgrades here as it is kind of automatic…
+ if (Files.File().Flagged(pkgCache::Flag::NotAutomatic) == false)
+ return true;
+ return false;
+}
+ /*}}}*/
+// VerIterator::NewestFile - Return the newest file version relation /*{{{*/
+// ---------------------------------------------------------------------
+/* This looks at the version numbers associated with all of the sources
+ this version is in and returns the highest.*/
+pkgCache::VerFileIterator pkgCache::VerIterator::NewestFile() const
+{
+ VerFileIterator Files = FileList();
+ VerFileIterator Highest = Files;
+ for (; Files.end() == false; ++Files)
+ {
+ if (Owner->VS->CmpReleaseVer(Files.File().Version(),Highest.File().Version()) > 0)
+ Highest = Files;
+ }
+
+ return Highest;
+}
+ /*}}}*/
+// VerIterator::RelStr - Release description string /*{{{*/
+// ---------------------------------------------------------------------
+/* This describes the version from a release-centric manner. The output is a
+ list of Label:Version/Archive */
+static std::string PkgFileIteratorToRelString(pkgCache::PkgFileIterator const &File)
+{
+ std::string Res;
+ if (File.Label() != 0)
+ Res = Res + File.Label() + ':';
+
+ if (File.Archive() != 0)
+ {
+ if (File.Version() == 0)
+ Res += File.Archive();
+ else
+ Res = Res + File.Version() + '/' + File.Archive();
+ }
+ else
+ {
+ // No release file, print the host name that this came from
+ if (File.Site() == 0 || File.Site()[0] == 0)
+ Res += "localhost";
+ else
+ Res += File.Site();
+ }
+ return Res;
+}
+string pkgCache::VerIterator::RelStr() const
+{
+ std::vector<std::string> RelStrs;
+ for (pkgCache::VerFileIterator I = this->FileList(); I.end() == false; ++I)
+ {
+ // Do not print 'not source' entries'
+ pkgCache::PkgFileIterator const File = I.File();
+ if (File.Flagged(pkgCache::Flag::NotSource))
+ continue;
+
+ std::string const RS = PkgFileIteratorToRelString(File);
+ if (std::find(RelStrs.begin(), RelStrs.end(), RS) != RelStrs.end())
+ continue;
+
+ RelStrs.push_back(RS);
+ }
+ std::ostringstream os;
+ if (likely(RelStrs.empty() == false))
+ {
+ std::copy(RelStrs.begin(), RelStrs.end()-1, std::ostream_iterator<std::string>(os, ", "));
+ os << *RelStrs.rbegin();
+ }
+ if (S->ParentPkg != 0)
+ os << " [" << Arch() << "]";
+ return os.str();
+}
+ /*}}}*/
+// VerIterator::MultiArchType - string representing MultiArch flag /*{{{*/
+const char * pkgCache::VerIterator::MultiArchType() const
+{
+ if ((S->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
+ return "same";
+ else if ((S->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
+ return "foreign";
+ else if ((S->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
+ return "allowed";
+ return "none";
+}
+ /*}}}*/
+// RlsFileIterator::RelStr - Return the release string /*{{{*/
+string pkgCache::RlsFileIterator::RelStr()
+{
+ string Res;
+ if (Version() != 0)
+ Res = Res + (Res.empty() == true?"v=":",v=") + Version();
+ if (Origin() != 0)
+ Res = Res + (Res.empty() == true?"o=":",o=") + Origin();
+ if (Archive() != 0)
+ Res = Res + (Res.empty() == true?"a=":",a=") + Archive();
+ if (Codename() != 0)
+ Res = Res + (Res.empty() == true?"n=":",n=") + Codename();
+ if (Label() != 0)
+ Res = Res + (Res.empty() == true?"l=":",l=") + Label();
+ return Res;
+}
+ /*}}}*/
+string pkgCache::PkgFileIterator::RelStr() /*{{{*/
+{
+ std::string Res;
+ if (ReleaseFile() == 0)
+ {
+ if (Component() != 0)
+ Res = Res + (Res.empty() == true?"a=":",a=") + Component();
+ }
+ else
+ {
+ Res = ReleaseFile().RelStr();
+ if (Component() != 0)
+ Res = Res + (Res.empty() == true?"c=":",c=") + Component();
+ }
+ if (Architecture() != 0)
+ Res = Res + (Res.empty() == true?"b=":",b=") + Architecture();
+ return Res;
+}
+ /*}}}*/
+// VerIterator::TranslatedDescriptionForLanguage - Return a DescIter for language/*{{{*/
+// ---------------------------------------------------------------------
+/* return a DescIter for the specified language
+ */
+pkgCache::DescIterator pkgCache::VerIterator::TranslatedDescriptionForLanguage(StringView lang) const
+{
+ for (pkgCache::DescIterator Desc = DescriptionList(); Desc.end() == false; ++Desc)
+ if (lang == Desc.LanguageCode())
+ return Desc;
+
+ if (lang == "en")
+ return TranslatedDescriptionForLanguage("");
+
+ return DescIterator();
+}
+
+ /*}}}*/
+// VerIterator::TranslatedDescription - Return the a DescIter for locale/*{{{*/
+// ---------------------------------------------------------------------
+/* return a DescIter for the current locale or the default if none is
+ * found
+ */
+pkgCache::DescIterator pkgCache::VerIterator::TranslatedDescription() const
+{
+ std::vector<string> const lang = APT::Configuration::getLanguages();
+ for (std::vector<string>::const_iterator l = lang.begin();
+ l != lang.end(); ++l)
+ {
+ pkgCache::DescIterator Desc = TranslatedDescriptionForLanguage(*l);
+ if (Desc.IsGood())
+ return Desc;
+ }
+
+ pkgCache::DescIterator Desc = TranslatedDescriptionForLanguage("");
+ if (Desc.IsGood())
+ return Desc;
+
+ return DescriptionList();
+}
+
+ /*}}}*/
+// VerIterator::IsSecurity - check if it is a security update /*{{{*/
+
+// See if this version is a security update. This also checks, for installed packages,
+// if any of the previous versions is a security update
+bool pkgCache::VerIterator::IsSecurityUpdate() const
+{
+ auto Pkg = ParentPkg();
+ auto Installed = Pkg.CurrentVer();
+
+ auto OtherVer = Pkg.VersionList();
+
+ // Advance to first version < our version
+ while (OtherVer->ID != S->ID)
+ ++OtherVer;
+
+ // Iterate over all versions < our version
+ for (; !OtherVer.end() && (Installed.end() || OtherVer->ID != Installed->ID); OtherVer++)
+ {
+ for (auto PF = OtherVer.FileList(); !PF.end(); PF++)
+ if (PF.File() && PF.File().Archive() != nullptr && APT::String::Endswith(PF.File().Archive(), "-security"))
+ return true;
+ }
+ return false;
+}
+
+ /*}}}*/
+pkgCache::~pkgCache() {}