diff options
Diffstat (limited to 'apt-pkg/pkgcache.cc')
-rw-r--r-- | apt-pkg/pkgcache.cc | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc new file mode 100644 index 000000000..b75fe6d94 --- /dev/null +++ b/apt-pkg/pkgcache.cc @@ -0,0 +1,360 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: pkgcache.cc,v 1.1 1998/07/02 02:58:12 jgg Exp $ +/* ###################################################################### + + Package Cache - Accessor code for the cache + + Please see doc/pkglib/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 managment. 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 seemlessly 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 <pkglib/pkgcache.h> +#include <pkglib/version.h> +#include <pkglib/error.h> +#include <system.h> + +#include <string> +#include <sys/stat.h> +#include <unistd.h> + /*}}}*/ + +// Cache::Header::Header - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* Simply initialize the header */ +pkgCache::Header::Header() +{ + Signature = 0x98FE76DC; + + /* Whenever the structures change the major version should be bumped, + whenever the generator changes the minor version should be bumped. */ + MajorVersion = 2; + MinorVersion = 0; + Dirty = true; + + HeaderSz = sizeof(pkgCache::Header); + PackageSz = sizeof(pkgCache::Package); + PackageFileSz = sizeof(pkgCache::PackageFile); + VersionSz = sizeof(pkgCache::Version); + DependencySz = sizeof(pkgCache::Dependency); + ProvidesSz = sizeof(pkgCache::Provides); + + PackageCount = 0; + VersionCount = 0; + DependsCount = 0; + PackageFileCount = 0; + + FileList = 0; + StringList = 0; + memset(HashTable,0,sizeof(HashTable)); + memset(Pools,0,sizeof(Pools)); +} + /*}}}*/ +// Cache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgCache::Header::CheckSizes(Header &Against) const +{ + if (HeaderSz == Against.HeaderSz && + PackageSz == Against.PackageSz && + PackageFileSz == Against.PackageFileSz && + VersionSz == Against.VersionSz && + DependencySz == Against.DependencySz && + ProvidesSz == Against.ProvidesSz) + return true; + return false; +} + /*}}}*/ + +// Cache::pkgCache - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgCache::pkgCache(MMap &Map) : Map(Map) +{ + ReMap(); +} + /*}}}*/ +// Cache::ReMap - Reopen the cache file /*{{{*/ +// --------------------------------------------------------------------- +/* If the file is already closed then this will open it open it. */ +bool pkgCache::ReMap() +{ + // Apply the typecasts. + HeaderP = (Header *)Map.Data(); + PkgP = (Package *)Map.Data(); + PkgFileP = (PackageFile *)Map.Data(); + VerP = (Version *)Map.Data(); + ProvideP = (Provides *)Map.Data(); + DepP = (Dependency *)Map.Data(); + StringItemP = (StringItem *)Map.Data(); + StrP = (char *)Map.Data(); + + cout << "Size is " << Map.Size() << endl; + if (Map.Size() == 0) + return false; + + // 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"); + + 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) */ +unsigned long pkgCache::sHash(string Str) +{ + unsigned long Hash = 0; + for (const char *I = Str.begin(); I != Str.end(); I++) + Hash += *I * ((Str.end() - I + 1)); + Header H; + return Hash % _count(H.HashTable); +} + +unsigned long pkgCache::sHash(const char *Str) +{ + unsigned long Hash = 0; + const char *End = Str + strlen(Str); + for (const char *I = Str; I != End; I++) + Hash += *I * ((End - I + 1)); + Header H; + return Hash % _count(H.HashTable); +} + + /*}}}*/ +// Cache::FindPkg - Locate a package by name /*{{{*/ +// --------------------------------------------------------------------- +/* Returns 0 on error, pointer to the package otherwise */ +pkgCache::PkgIterator pkgCache::FindPkg(string Name) +{ + // Look at the hash bucket + Package *Pkg = PkgP + HeaderP->HashTable[Hash(Name)]; + for (; Pkg != PkgP; Pkg = PkgP + Pkg->NextPackage) + { + if (Pkg->Name != 0 && StrP + Pkg->Name == Name) + return PkgIterator(*this,Pkg); + } + return PkgIterator(*this,0); +} + /*}}}*/ + +// Cache::PkgIterator - operator ++ - Postfix incr /*{{{*/ +// --------------------------------------------------------------------- +/* This will advance to the next logical package in the hash table. */ +void pkgCache::PkgIterator::operator ++(int) +{ + // Follow the current links + if (Pkg != Owner->PkgP) + Pkg = Owner->PkgP + Pkg->NextPackage; + + // Follow the hash table + while (Pkg == Owner->PkgP && HashIndex < (signed)_count(Owner->HeaderP->HashTable)) + { + HashIndex++; + Pkg = Owner->PkgP + Owner->HeaderP->HashTable[HashIndex]; + } +}; + /*}}}*/ +// Bases for iterator classes /*{{{*/ +void pkgCache::VerIterator::_dummy() {} +void pkgCache::DepIterator::_dummy() {} +void pkgCache::PrvIterator::_dummy() {} + /*}}}*/ +// 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 (Pkg->CurrentState == pkgSTATE_UnPacked || + Pkg->CurrentState == pkgSTATE_HalfConfigured) + return NeedsConfigure; + + if (Pkg->CurrentState == pkgSTATE_UnInstalled || + Pkg->CurrentState == pkgSTATE_HalfInstalled || + Pkg->InstState != pkgSTATE_Ok) + return NeedsUnpack; + + return NeedsNothing; +} + /*}}}*/ +// DepIterator::IsCritical - Returns true if the dep is important /*{{{*/ +// --------------------------------------------------------------------- +/* Currently critical deps are defined as depends, predepends and + conflicts. */ +bool pkgCache::DepIterator::IsCritical() +{ + if (Dep->Type == pkgDEP_Conflicts || Dep->Type == pkgDEP_Depends || + Dep->Type == pkgDEP_PreDepends) + return true; + return false; +} + /*}}}*/ +// 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 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 */ +bool pkgCache::DepIterator::SmartTargetPkg(PkgIterator &Result) +{ + 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; + } + + // Check for non dups + if (P.end() != true) + return true; + Result = PStart.OwnerPkg(); + return false; +} + /*}}}*/ +// DepIterator::AllTargets - Returns the set of all possible targets /*{{{*/ +// --------------------------------------------------------------------- +/* This is a more usefull 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. */ +pkgCache::Version **pkgCache::DepIterator::AllTargets() +{ + 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 (pkgCheckDep(TargetVer(),I.VerStr(),Dep->CompareOp) == false) + continue; + + if (Dep->Type == pkgDEP_Conflicts && ParentPkg() == I.ParentPkg()) + continue; + + Size++; + if (Res != 0) + *End++ = I; + } + + // Follow all provides + for (PrvIterator I = DPkg.ProvidesList(); I.end() == false; I++) + { + if (pkgCheckDep(TargetVer(),I.ProvideVersion(),Dep->CompareOp) == false) + continue; + + if (Dep->Type == pkgDEP_Conflicts && ParentPkg() == I.OwnerPkg()) + 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; +} + /*}}}*/ +// 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; +} + /*}}}*/ +// PkgFileIterator::IsOk - Checks if the cache is in sync with the file /*{{{*/ +// --------------------------------------------------------------------- +/* This stats the file and compares its stats with the ones that were + stored during generation. Date checks should probably also be + included here. */ +bool pkgCache::PkgFileIterator::IsOk() +{ + struct stat Buf; + if (stat(FileName(),&Buf) != 0) + return false; + + if (Buf.st_size != (signed)File->Size || Buf.st_mtime != File->mtime) + return false; + + return true; +} + /*}}}*/ |