diff options
Diffstat (limited to '')
-rw-r--r-- | apt-private/private-cacheset.cc | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/apt-private/private-cacheset.cc b/apt-private/private-cacheset.cc new file mode 100644 index 0000000..1b2834e --- /dev/null +++ b/apt-private/private-cacheset.cc @@ -0,0 +1,505 @@ +#include <config.h> + +#include <apt-pkg/cacheset.h> +#include <apt-pkg/aptconfiguration.h> +#include <apt-pkg/cachefile.h> +#include <apt-pkg/cachefilter.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/depcache.h> +#include <apt-pkg/pkgcache.h> +#include <apt-pkg/policy.h> +#include <apt-pkg/progress.h> +#include <apt-pkg/strutl.h> + +#include <apt-private/private-cacheset.h> + +#include <stddef.h> + +#include <apti18n.h> + +bool GetLocalitySortedVersionSet(pkgCacheFile &CacheFile, /*{{{*/ + APT::VersionContainerInterface * const vci, + OpProgress * const progress) +{ + Matcher null_matcher = Matcher(); + return GetLocalitySortedVersionSet(CacheFile, vci, + null_matcher, progress); +} +bool GetLocalitySortedVersionSet(pkgCacheFile &CacheFile, + APT::VersionContainerInterface * const vci, + Matcher &matcher, + OpProgress * const progress) +{ + pkgCache * const Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == nullptr)) + return false; + if (progress != nullptr) + progress->SubProgress(Cache->Head().PackageCount, _("Sorting")); + + pkgDepCache * const DepCache = CacheFile.GetDepCache(); + if (unlikely(DepCache == nullptr)) + return false; + APT::CacheSetHelper helper(false); + + int Done=0; + + bool const insertCurrentVer = _config->FindB("APT::Cmd::Installed", false); + bool const insertUpgradable = _config->FindB("APT::Cmd::Upgradable", false); + bool const insertManualInstalled = _config->FindB("APT::Cmd::Manual-Installed", false); + + for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P) + { + if (progress != NULL) + { + if (Done % 500 == 0) + progress->Progress(Done); + ++Done; + } + + // exclude virtual pkgs + if (P->VersionList == 0) + continue; + + if ((matcher)(P) == false) + continue; + + pkgDepCache::StateCache &state = (*DepCache)[P]; + if (insertCurrentVer == true) + { + if (P->CurrentVer != 0) + vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::INSTALLED, helper); + } + else if (insertUpgradable == true) + { + if(P.CurrentVer() && state.Upgradable()) + vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::CANDIDATE, helper); + } + else if (insertManualInstalled == true) + { + if (P.CurrentVer() && + ((*DepCache)[P].Flags & pkgCache::Flag::Auto) == false) + vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::CANDIDATE, helper); + } + else + { + if (vci->FromPackage(vci, CacheFile, P, APT::CacheSetHelper::CANDIDATE, helper) == false) + { + // no candidate, this may happen for packages in + // dpkg "deinstall ok config-file" state - we pick the first ver + // (which should be the only one) + vci->insert(P.VersionList()); + } + } + } + if (progress != NULL) + progress->Done(); + return true; +} + /*}}}*/ + +// CacheSetHelper saving virtual packages /*{{{*/ +pkgCache::VerIterator CacheSetHelperVirtuals::canNotGetVersion( + enum CacheSetHelper::VerSelector const select, + pkgCacheFile &Cache, + pkgCache::PkgIterator const &Pkg) +{ + switch (select) + { + case VERSIONNUMBER: + case RELEASE: + case INSTALLED: + case CANDIDATE: + case NEWEST: + case ALL: + virtualPkgs.insert(Pkg); + break; + case CANDANDINST: + case CANDINST: + case INSTCAND: + break; + } + return CacheSetHelper::canNotGetVersion(select, Cache, Pkg); +} +void CacheSetHelperVirtuals::canNotFindVersion( + enum CacheSetHelper::VerSelector const select, + APT::VersionContainerInterface * vci, + pkgCacheFile &Cache, + pkgCache::PkgIterator const &Pkg) +{ + if (select == NEWEST || select == CANDIDATE || select == ALL) + virtualPkgs.insert(Pkg); + return CacheSetHelper::canNotFindVersion(select, vci, Cache, Pkg); +} +static pkgCache::PkgIterator canNotFindPkgName_impl(pkgCacheFile &Cache, std::string const &str) +{ + std::string pkg = str; + size_t const archfound = pkg.find_last_of(':'); + std::string arch; + if (archfound != std::string::npos) { + arch = pkg.substr(archfound+1); + pkg.erase(archfound); + if (arch == "all" || arch == "native") + arch = _config->Find("APT::Architecture"); + } + + // If we don't find 'foo:amd64' look for 'foo:amd64:any'. + // Note: we prepare for an error here as if foo:amd64 does not exist, + // but foo:amd64:any it means that this package is only referenced in a + // (architecture specific) dependency. We do not add to virtualPkgs directly + // as we can't decide from here which error message has to be printed. + // FIXME: This doesn't match 'barbarian' architectures + pkgCache::PkgIterator Pkg(Cache, 0); + std::vector<std::string> const archs = APT::Configuration::getArchitectures(); + if (archfound == std::string::npos) + { + for (auto const &a : archs) + { + Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + a, "any"); + if (Pkg.end() == false && Pkg->ProvidesList != 0) + break; + } + if (Pkg.end() == true) + for (auto const &a : archs) + { + Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + a, "any"); + if (Pkg.end() == false) + break; + } + } + else + { + Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + arch, "any"); + if (Pkg.end() == true) + { + APT::CacheFilter::PackageArchitectureMatchesSpecification pams(arch); + for (auto const &a : archs) + { + if (pams(a.c_str()) == false) + continue; + Pkg = Cache.GetPkgCache()->FindPkg(pkg + ':' + a, "any"); + if (Pkg.end() == false) + break; + } + } + } + return Pkg; +} +pkgCache::PkgIterator CacheSetHelperVirtuals::canNotFindPkgName(pkgCacheFile &Cache, std::string const &str) +{ + pkgCache::PkgIterator const Pkg = canNotFindPkgName_impl(Cache, str); + if (Pkg.end()) + return APT::CacheSetHelper::canNotFindPkgName(Cache, str); + return Pkg; +} +CacheSetHelperVirtuals::CacheSetHelperVirtuals(bool const ShowErrors, GlobalError::MsgType const &ErrorType) : + CacheSetHelper{ShowErrors, ErrorType} +{} + /*}}}*/ + +// CacheSetHelperAPTGet - responsible for message telling from the CacheSets/*{{{*/ +CacheSetHelperAPTGet::CacheSetHelperAPTGet(std::ostream &pout) : + APT::CacheSetHelper{true}, out(pout) +{ + explicitlyNamed = true; +} +void CacheSetHelperAPTGet::showPackageSelection(pkgCache::PkgIterator const &pkg, enum PkgSelector const select, + std::string const &pattern) +{ + switch (select) + { + case REGEX: + showRegExSelection(pkg, pattern); + break; + case TASK: + showTaskSelection(pkg, pattern); + break; + case FNMATCH: + showFnmatchSelection(pkg, pattern); + break; + default: + APT::CacheSetHelper::showPackageSelection(pkg, select, pattern); + break; + } +} +void CacheSetHelperAPTGet::showTaskSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern) +{ + ioprintf(out, _("Note, selecting '%s' for task '%s'\n"), + Pkg.FullName(true).c_str(), pattern.c_str()); + explicitlyNamed = false; +} +void CacheSetHelperAPTGet::showFnmatchSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern) +{ + ioprintf(out, _("Note, selecting '%s' for glob '%s'\n"), + Pkg.FullName(true).c_str(), pattern.c_str()); + explicitlyNamed = false; +} +void CacheSetHelperAPTGet::showRegExSelection(pkgCache::PkgIterator const &Pkg, std::string const &pattern) +{ + ioprintf(out, _("Note, selecting '%s' for regex '%s'\n"), + Pkg.FullName(true).c_str(), pattern.c_str()); + explicitlyNamed = false; +} +void CacheSetHelperAPTGet::showVersionSelection(pkgCache::PkgIterator const &Pkg, + pkgCache::VerIterator const &Ver, enum VerSelector const select, std::string const &pattern) +{ + switch (select) + { + case VERSIONNUMBER: + if (pattern == Ver.VerStr()) + return; + /* fall through */ + case RELEASE: + selectedByRelease.push_back(make_pair(Ver, pattern)); + break; + default: + return APT::CacheSetHelper::showVersionSelection(Pkg, Ver, select, pattern); + } +} + +bool CacheSetHelperAPTGet::showVirtualPackageErrors(pkgCacheFile &Cache) +{ + if (virtualPkgs.empty() == true) + return true; + for (APT::PackageSet::const_iterator Pkg = virtualPkgs.begin(); + Pkg != virtualPkgs.end(); ++Pkg) { + if (Pkg->ProvidesList != 0) { + ioprintf(c1out,_("Package %s is a virtual package provided by:\n"), + Pkg.FullName(true).c_str()); + + pkgCache::PrvIterator I = Pkg.ProvidesList(); + unsigned short provider = 0; + for (; I.end() == false; ++I) { + pkgCache::PkgIterator const OPkg = I.OwnerPkg(); + + if (Cache[OPkg].CandidateVerIter(Cache) == I.OwnerVer()) + { + c1out << " " << OPkg.FullName(true) << ' ' << I.OwnerVer().VerStr(); + if (I->ProvideVersion != 0) + c1out << " (= " << I.ProvideVersion() << ")"; + if (Cache[OPkg].Install() == true && Cache[OPkg].NewInstall() == false) + c1out << _(" [Installed]"); + c1out << std::endl; + ++provider; + } + } + // if we found no candidate which provide this package, show non-candidates + if (provider == 0) + for (I = Pkg.ProvidesList(); I.end() == false; ++I) + { + c1out << " " << I.OwnerPkg().FullName(true) << " " << I.OwnerVer().VerStr(); + if (I->ProvideVersion != 0) + c1out << " (= " << I.ProvideVersion() << ")"; + c1out << _(" [Not candidate version]") << std::endl; + } + else + out << _("You should explicitly select one to install.") << std::endl; + } else { + ioprintf(c1out, + _("Package %s is not available, but is referred to by another package.\n" + "This may mean that the package is missing, has been obsoleted, or\n" + "is only available from another source\n"),Pkg.FullName(true).c_str()); + + std::vector<bool> Seen(Cache.GetPkgCache()->Head().PackageCount, false); + APT::PackageList pkglist; + for (pkgCache::DepIterator Dep = Pkg.RevDependsList(); + Dep.end() == false; ++Dep) { + if (Dep->Type != pkgCache::Dep::Replaces) + continue; + pkgCache::PkgIterator const DP = Dep.ParentPkg(); + if (Seen[DP->ID] == true) + continue; + Seen[DP->ID] = true; + pkglist.insert(DP); + } + ShowList(c1out, _("However the following packages replace it:"), pkglist, + &AlwaysTrue, &PrettyFullName, &EmptyString); + } + c1out << std::endl; + } + return false; +} +pkgCache::VerIterator CacheSetHelperAPTGet::canNotGetVersion(enum VerSelector const select, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) +{ + switch (select) + { + case NEWEST: + return canNotFindNewestVer(Cache, Pkg); + case CANDIDATE: + return canNotFindCandidateVer(Cache, Pkg); + case VERSIONNUMBER: + return canNotFindVersionNumber(Cache, Pkg, getLastVersionMatcher()); + case RELEASE: + return canNotFindVersionRelease(Cache, Pkg, getLastVersionMatcher()); + default: + return APT::CacheSetHelper::canNotGetVersion(select, Cache, Pkg); + } +} +void CacheSetHelperAPTGet::canNotFindVersion(enum VerSelector const select, APT::VersionContainerInterface * const vci, pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) +{ + switch (select) + { + case NEWEST: + canNotFindNewestVer(Cache, Pkg); + break; + case CANDIDATE: + canNotFindCandidateVer(Cache, Pkg); + break; + default: + return APT::CacheSetHelper::canNotFindVersion(select, vci, Cache, Pkg); + } +} + +pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindVersionNumber(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, std::string const &verstr) +{ + APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::VERSIONNUMBER); + if (not verset.empty()) + return *(verset.begin()); + else if (ShowError) + { + auto const V = canNotGetVerFromVersionNumber(Cache, Pkg, verstr); + if (not V.end()) + return V; + virtualPkgs.insert(Pkg); + } + return pkgCache::VerIterator(Cache, 0); +} +pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindVersionRelease(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, std::string const &verstr) +{ + APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::RELEASE); + if (not verset.empty()) + return *(verset.begin()); + else if (ShowError) + { + auto const V = canNotGetVerFromRelease(Cache, Pkg, verstr); + if (not V.end()) + return V; + virtualPkgs.insert(Pkg); + } + return pkgCache::VerIterator(Cache, 0); +} +pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindCandidateVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) +{ + APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::CANDIDATE); + if (verset.empty() == false) + return *(verset.begin()); + else if (ShowError == true) { + _error->Error(_("Package '%s' has no installation candidate"),Pkg.FullName(true).c_str()); + virtualPkgs.insert(Pkg); + } + return pkgCache::VerIterator(Cache, 0); +} +pkgCache::VerIterator CacheSetHelperAPTGet::canNotFindNewestVer(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg) +{ + if (Pkg->ProvidesList != 0) + { + APT::VersionSet const verset = tryVirtualPackage(Cache, Pkg, CacheSetHelper::NEWEST); + if (verset.empty() == false) + return *(verset.begin()); + if (ShowError == true) + ioprintf(out, _("Virtual packages like '%s' can't be removed\n"), Pkg.FullName(true).c_str()); + } + else + { + pkgCache::GrpIterator Grp = Pkg.Group(); + pkgCache::PkgIterator P = Grp.PackageList(); + for (; P.end() != true; P = Grp.NextPkg(P)) + { + if (P == Pkg) + continue; + if (P->CurrentVer != 0) { + // TRANSLATORS: Note, this is not an interactive question + ioprintf(c1out,_("Package '%s' is not installed, so not removed. Did you mean '%s'?\n"), + Pkg.FullName(true).c_str(), P.FullName(true).c_str()); + break; + } + } + if (P.end() == true) + ioprintf(c1out,_("Package '%s' is not installed, so not removed\n"),Pkg.FullName(true).c_str()); + } + return pkgCache::VerIterator(Cache, 0); +} +APT::VersionSet CacheSetHelperAPTGet::tryVirtualPackage(pkgCacheFile &Cache, pkgCache::PkgIterator const &Pkg, + CacheSetHelper::VerSelector const select) +{ + /* If this is a virtual package see if we have a single matching provider + (ignoring multiple matches from the same package due to e.g. M-A) */ + if (Pkg->ProvidesList == 0) + return APT::VersionSet{}; + + auto const oldShowError = showErrors(false); + APT::VersionVector verset; + auto const lastmatcher = getLastVersionMatcher(); + for (auto P = Pkg.ProvidesList(); not P.end(); ++P) + { + auto V = P.OwnerVer(); + switch (select) + { + case RELEASE: + for (auto File = V.FileList(); not File.end(); ++File) + if ((File.File().Archive() != nullptr && lastmatcher == File.File().Archive()) || + (File.File().Codename() != nullptr && lastmatcher == File.File().Codename())) + { + verset.push_back(V); + break; + } + break; + case VERSIONNUMBER: + if (P->ProvideVersion != 0 && lastmatcher == P.ProvideVersion()) + verset.push_back(V); + break; + default: + if (Cache[V.ParentPkg()].CandidateVerIter(Cache) == V) + verset.push_back(V); + break; + } + } + // do not change the candidate if we have more than one option for this package + if (select == VERSIONNUMBER || select == RELEASE) + for (auto const &V : verset) + if (std::count_if(verset.begin(), verset.end(), [Pkg = V.ParentPkg()](auto const &v) { return v.ParentPkg() == Pkg; }) == 1) + Cache->SetCandidateVersion(V); + showErrors(oldShowError); + + pkgCache::VerIterator Choosen; + for (auto const &Ver : verset) + { + if (Choosen.end()) + Choosen = Ver; + else + { + auto const ChoosenPkg = Choosen.ParentPkg(); + auto const AltPkg = Ver.ParentPkg(); + // seeing two different packages makes it not simple anymore + if (ChoosenPkg->Group != AltPkg->Group) + return APT::VersionSet{}; + // do we already have the requested arch? + if (strcmp(Pkg.Arch(), ChoosenPkg.Arch()) == 0 || + strcmp(ChoosenPkg.Arch(), "all") == 0) + continue; + // see which architecture we prefer more and switch to it + std::vector<std::string> archs = APT::Configuration::getArchitectures(); + if (std::find(archs.begin(), archs.end(), AltPkg.Arch()) < std::find(archs.begin(), archs.end(), ChoosenPkg.Arch())) + Choosen = Ver; + } + } + if (Choosen.end()) + return APT::VersionSet{}; + + ioprintf(out, _("Note, selecting '%s' instead of '%s'\n"), + Choosen.ParentPkg().FullName(true).c_str(), Pkg.FullName(true).c_str()); + return { Choosen }; +} +pkgCache::PkgIterator CacheSetHelperAPTGet::canNotFindPkgName(pkgCacheFile &Cache, std::string const &str) +{ + pkgCache::PkgIterator Pkg = canNotFindPkgName_impl(Cache, str); + if (Pkg.end()) + { + Pkg = APT::CacheSetHelper::canNotFindPkgName(Cache, str); + if (Pkg.end() && ShowError) + { + notFound.insert(str); + } + } + return Pkg; +} + /*}}}*/ |