X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/e1b74f61dfb6980d643cb7c666c761ff3bda2f1e..6f43fac607c94f423f1d01708ecf947a8a303d38:/cmdline/apt-cache.cc diff --git a/cmdline/apt-cache.cc b/cmdline/apt-cache.cc index a4f34c61a..b8892d23d 100644 --- a/cmdline/apt-cache.cc +++ b/cmdline/apt-cache.cc @@ -1,77 +1,255 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: apt-cache.cc,v 1.8 1998/09/26 05:34:29 jgg Exp $ +// $Id: apt-cache.cc,v 1.72 2004/04/30 04:34:03 mdz Exp $ /* ###################################################################### apt-cache - Manages the cache files apt-cache provides some functions fo manipulating the cache files. - It uses the command line interface common to all the APT tools. The - only really usefull function right now is dumpavail which is used - by the dselect method. Everything else is ment as a debug aide. + It uses the command line interface common to all the APT tools. Returns 100 on failure, 0 on success. ##################################################################### */ /*}}}*/ // 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 +#include +#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /*}}}*/ -// DumpPackage - Show a dump of a package record /*{{{*/ +using namespace std; + +// LocalitySort - Sort a version list by package file locality /*{{{*/ // --------------------------------------------------------------------- /* */ -bool DumpPackage(pkgCache &Cache,CommandLine &CmdL) +int LocalityCompare(const void *a, const void *b) +{ + pkgCache::VerFile *A = *(pkgCache::VerFile **)a; + pkgCache::VerFile *B = *(pkgCache::VerFile **)b; + + if (A == 0 && B == 0) + return 0; + if (A == 0) + return 1; + if (B == 0) + return -1; + + if (A->File == B->File) + return A->Offset - B->Offset; + return A->File - B->File; +} + +void LocalitySort(pkgCache::VerFile **begin, + unsigned long Count,size_t Size) { - for (const char **I = CmdL.FileList + 1; *I != 0; I++) + qsort(begin,Count,Size,LocalityCompare); +} + +void LocalitySort(pkgCache::DescFile **begin, + unsigned long Count,size_t Size) +{ + qsort(begin,Count,Size,LocalityCompare); +} + /*}}}*/ +// UnMet - Show unmet dependencies /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool ShowUnMet(pkgCache::VerIterator const &V, bool const Important) +{ + bool Header = false; + for (pkgCache::DepIterator D = V.DependsList(); D.end() == false;) + { + // Collect or groups + pkgCache::DepIterator Start; + pkgCache::DepIterator End; + D.GlobOr(Start,End); + + // Important deps only + if (Important == true) + if (End->Type != pkgCache::Dep::PreDepends && + End->Type != pkgCache::Dep::Depends) + continue; + + // Skip conflicts and replaces + if (End.IsNegative() == true) + continue; + + // Verify the or group + bool OK = false; + pkgCache::DepIterator RealStart = Start; + do + { + // See if this dep is Ok + pkgCache::Version **VList = Start.AllTargets(); + if (*VList != 0) + { + OK = true; + delete [] VList; + break; + } + delete [] VList; + + if (Start == End) + break; + ++Start; + } + while (1); + + // The group is OK + if (OK == true) + continue; + + // Oops, it failed.. + if (Header == false) + ioprintf(cout,_("Package %s version %s has an unmet dep:\n"), + V.ParentPkg().FullName(true).c_str(),V.VerStr()); + Header = true; + + // Print out the dep type + cout << " " << End.DepType() << ": "; + + // Show the group + Start = RealStart; + do + { + cout << Start.TargetPkg().FullName(true); + if (Start.TargetVer() != 0) + cout << " (" << Start.CompType() << " " << Start.TargetVer() << + ")"; + if (Start == End) + break; + cout << " | "; + ++Start; + } + while (1); + + cout << endl; + } + return true; +} +bool UnMet(CommandLine &CmdL) +{ + bool const Important = _config->FindB("APT::Cache::Important",false); + + pkgCacheFile CacheFile; + if (unlikely(CacheFile.GetPkgCache() == NULL)) + return false; + + if (CmdL.FileSize() <= 1) { - pkgCache::PkgIterator Pkg = Cache.FindPkg(*I); - if (Pkg.end() == true) + for (pkgCache::PkgIterator P = CacheFile.GetPkgCache()->PkgBegin(); P.end() == false; ++P) + for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V) + if (ShowUnMet(V, Important) == false) + return false; + } + else + { + CacheSetHelperVirtuals helper(true, GlobalError::NOTICE); + APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, + APT::VersionList::CANDIDATE, helper); + for (APT::VersionList::iterator V = verset.begin(); V != verset.end(); ++V) + if (ShowUnMet(V, Important) == false) + return false; + } + return true; +} + /*}}}*/ +// DumpPackage - Show a dump of a package record /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool DumpPackage(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + APT::CacheSetHelper helper(true, GlobalError::NOTICE); + APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1, helper); + + for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) + { + cout << "Package: " << Pkg.FullName(true) << endl; + cout << "Versions: " << endl; + for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; ++Cur) { - _error->Warning("Unable to locate package %s",*I); - continue; + cout << Cur.VerStr(); + for (pkgCache::VerFileIterator Vf = Cur.FileList(); Vf.end() == false; ++Vf) + cout << " (" << Vf.File().FileName() << ")"; + cout << endl; + for (pkgCache::DescIterator D = Cur.DescriptionList(); D.end() == false; ++D) + { + cout << " Description Language: " << D.LanguageCode() << endl + << " File: " << D.FileList().File().FileName() << endl + << " MD5: " << D.md5() << endl; + } + cout << endl; } - - cout << "Package: " << Pkg.Name() << endl; - cout << "Versions: "; - for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; Cur++) - cout << Cur.VerStr() << ','; + cout << endl; cout << "Reverse Depends: " << endl; - for (pkgCache::DepIterator D = Pkg.RevDependsList(); D.end() != true; D++) - cout << " " << D.ParentPkg().Name() << ',' << D.TargetPkg().Name() << endl; - + for (pkgCache::DepIterator D = Pkg.RevDependsList(); D.end() != true; ++D) + { + cout << " " << D.ParentPkg().FullName(true) << ',' << D.TargetPkg().FullName(true); + if (D->Version != 0) + cout << ' ' << DeNull(D.TargetVer()) << endl; + else + cout << endl; + } + cout << "Dependencies: " << endl; - for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; Cur++) + for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; ++Cur) { cout << Cur.VerStr() << " - "; - for (pkgCache::DepIterator Dep = Cur.DependsList(); Dep.end() != true; Dep++) - cout << Dep.TargetPkg().Name() << " (" << (int)Dep->CompareOp << " " << Dep.TargetVer() << ") "; + for (pkgCache::DepIterator Dep = Cur.DependsList(); Dep.end() != true; ++Dep) + cout << Dep.TargetPkg().FullName(true) << " (" << (int)Dep->CompareOp << " " << DeNull(Dep.TargetVer()) << ") "; cout << endl; } cout << "Provides: " << endl; - for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; Cur++) + for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; ++Cur) { cout << Cur.VerStr() << " - "; - for (pkgCache::PrvIterator Prv = Cur.ProvidesList(); Prv.end() != true; Prv++) - cout << Prv.ParentPkg().Name() << " "; + for (pkgCache::PrvIterator Prv = Cur.ProvidesList(); Prv.end() != true; ++Prv) + cout << Prv.ParentPkg().FullName(true) << " "; cout << endl; } cout << "Reverse Provides: " << endl; - for (pkgCache::PrvIterator Prv = Pkg.ProvidesList(); Prv.end() != true; Prv++) - cout << Prv.OwnerPkg().Name() << " " << Prv.OwnerVer().VerStr(); - cout << endl; + for (pkgCache::PrvIterator Prv = Pkg.ProvidesList(); Prv.end() != true; ++Prv) + cout << Prv.OwnerPkg().FullName(true) << " " << Prv.OwnerVer().VerStr() << endl; } return true; @@ -80,17 +258,25 @@ bool DumpPackage(pkgCache &Cache,CommandLine &CmdL) // Stats - Dump some nice statistics /*{{{*/ // --------------------------------------------------------------------- /* */ -bool Stats(pkgCache &Cache) +bool Stats(CommandLine &Cmd) { - cout << "Total Package Names : " << Cache.Head().PackageCount << endl; - pkgCache::PkgIterator I = Cache.PkgBegin(); - + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL)) + return false; + + cout << _("Total package names: ") << Cache->Head().GroupCount << " (" << + SizeToStr(Cache->Head().GroupCount*Cache->Head().GroupSz) << ')' << endl + << _("Total package structures: ") << Cache->Head().PackageCount << " (" << + SizeToStr(Cache->Head().PackageCount*Cache->Head().PackageSz) << ')' << endl; + int Normal = 0; int Virtual = 0; int NVirt = 0; int DVirt = 0; int Missing = 0; - for (;I.end() != true; I++) + pkgCache::PkgIterator I = Cache->PkgBegin(); + for (;I.end() != true; ++I) { if (I->VersionList != 0 && I->ProvidesList == 0) { @@ -121,41 +307,113 @@ bool Stats(pkgCache &Cache) continue; } } - cout << " Normal Packages: " << Normal << endl; - cout << " Pure Virtual Packages: " << Virtual << endl; - cout << " Single Virtual Packages: " << DVirt << endl; - cout << " Mixed Virtual Packages: " << NVirt << endl; - cout << " Missing: " << Missing << endl; + cout << _(" Normal packages: ") << Normal << endl; + cout << _(" Pure virtual packages: ") << Virtual << endl; + cout << _(" Single virtual packages: ") << DVirt << endl; + cout << _(" Mixed virtual packages: ") << NVirt << endl; + cout << _(" Missing: ") << Missing << endl; + + cout << _("Total distinct versions: ") << Cache->Head().VersionCount << " (" << + SizeToStr(Cache->Head().VersionCount*Cache->Head().VersionSz) << ')' << endl; + cout << _("Total distinct descriptions: ") << Cache->Head().DescriptionCount << " (" << + SizeToStr(Cache->Head().DescriptionCount*Cache->Head().DescriptionSz) << ')' << endl; + cout << _("Total dependencies: ") << Cache->Head().DependsCount << " (" << + SizeToStr(Cache->Head().DependsCount*Cache->Head().DependencySz) << ')' << endl; + + cout << _("Total ver/file relations: ") << Cache->Head().VerFileCount << " (" << + SizeToStr(Cache->Head().VerFileCount*Cache->Head().VerFileSz) << ')' << endl; + cout << _("Total Desc/File relations: ") << Cache->Head().DescFileCount << " (" << + SizeToStr(Cache->Head().DescFileCount*Cache->Head().DescFileSz) << ')' << endl; + cout << _("Total Provides mappings: ") << Cache->Head().ProvidesCount << " (" << + SizeToStr(Cache->Head().ProvidesCount*Cache->Head().ProvidesSz) << ')' << endl; + + // String list stats + unsigned long Size = 0; + unsigned long Count = 0; + for (pkgCache::StringItem *I = Cache->StringItemP + Cache->Head().StringList; + I!= Cache->StringItemP; I = Cache->StringItemP + I->NextItem) + { + Count++; + Size += strlen(Cache->StrP + I->String) + 1; + } + cout << _("Total globbed strings: ") << Count << " (" << SizeToStr(Size) << ')' << endl; + + unsigned long DepVerSize = 0; + for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P) + { + for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V) + { + for (pkgCache::DepIterator D = V.DependsList(); D.end() == false; ++D) + { + if (D->Version != 0) + DepVerSize += strlen(D.TargetVer()) + 1; + } + } + } + cout << _("Total dependency version space: ") << SizeToStr(DepVerSize) << endl; + + unsigned long Slack = 0; + for (int I = 0; I != 7; I++) + Slack += Cache->Head().Pools[I].ItemSize*Cache->Head().Pools[I].Count; + cout << _("Total slack space: ") << SizeToStr(Slack) << endl; + + unsigned long Total = 0; + Total = Slack + Size + Cache->Head().DependsCount*Cache->Head().DependencySz + + Cache->Head().VersionCount*Cache->Head().VersionSz + + Cache->Head().PackageCount*Cache->Head().PackageSz + + Cache->Head().VerFileCount*Cache->Head().VerFileSz + + Cache->Head().ProvidesCount*Cache->Head().ProvidesSz; + cout << _("Total space accounted for: ") << SizeToStr(Total) << endl; - cout << "Total Distinct Versions: " << Cache.Head().VersionCount << endl; - cout << "Total Dependencies: " << Cache.Head().DependsCount << endl; return true; } /*}}}*/ // Dump - show everything /*{{{*/ // --------------------------------------------------------------------- -/* */ -bool Dump(pkgCache &Cache) +/* This is worthless except fer debugging things */ +bool Dump(CommandLine &Cmd) { - for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; P++) + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL)) + return false; + + std::cout << "Using Versioning System: " << Cache->VS->Label << std::endl; + + for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P) { - cout << "Package: " << P.Name() << endl; - for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; V++) + std::cout << "Package: " << P.FullName(true) << std::endl; + for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V) { - cout << " Version: " << V.VerStr() << endl; - cout << " File: " << V.FileList().File().FileName() << endl; - for (pkgCache::DepIterator D = V.DependsList(); D.end() == false; D++) - cout << " Depends: " << D.TargetPkg().Name() << ' ' << D.TargetVer() << endl; + std::cout << " Version: " << V.VerStr() << std::endl; + std::cout << " File: " << V.FileList().File().FileName() << std::endl; + for (pkgCache::DepIterator D = V.DependsList(); D.end() == false; ++D) + std::cout << " Depends: " << D.TargetPkg().FullName(true) << ' ' << + DeNull(D.TargetVer()) << std::endl; + for (pkgCache::DescIterator D = V.DescriptionList(); D.end() == false; ++D) + { + std::cout << " Description Language: " << D.LanguageCode() << std::endl + << " File: " << D.FileList().File().FileName() << std::endl + << " MD5: " << D.md5() << std::endl; + } } } - for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++) + for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F.end() == false; ++F) { - cout << "File: " << F.FileName() << endl; - cout << " Size: " << F->Size << endl; - cout << " ID: " << F->ID << endl; - cout << " Flags: " << F->Flags << endl; - cout << " Time: " << ctime(&F->mtime) << endl; + std::cout << "File: " << F.FileName() << std::endl; + std::cout << " Type: " << F.IndexType() << std::endl; + std::cout << " Size: " << F->Size << std::endl; + std::cout << " ID: " << F->ID << std::endl; + std::cout << " Flags: " << F->Flags << std::endl; + std::cout << " Time: " << TimeRFC1123(F->mtime) << std::endl; + std::cout << " Archive: " << DeNull(F.Archive()) << std::endl; + std::cout << " Component: " << DeNull(F.Component()) << std::endl; + std::cout << " Version: " << DeNull(F.Version()) << std::endl; + std::cout << " Origin: " << DeNull(F.Origin()) << std::endl; + std::cout << " Site: " << DeNull(F.Site()) << std::endl; + std::cout << " Label: " << DeNull(F.Label()) << std::endl; + std::cout << " Architecture: " << DeNull(F.Architecture()) << std::endl; } return true; @@ -163,235 +421,1420 @@ bool Dump(pkgCache &Cache) /*}}}*/ // DumpAvail - Print out the available list /*{{{*/ // --------------------------------------------------------------------- -/* This is needed to make dpkg --merge happy */ -bool DumpAvail(pkgCache &Cache) +/* This is needed to make dpkg --merge happy.. I spent a bit of time to + make this run really fast, perhaps I went a little overboard.. */ +bool DumpAvail(CommandLine &Cmd) { - unsigned char *Buffer = new unsigned char[Cache.HeaderP->MaxVerFileSize]; + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL || CacheFile.BuildPolicy() == false)) + return false; - for (pkgCache::PkgFileIterator I = Cache.FileBegin(); I.end() == false; I++) - { - if ((I->Flags & pkgCache::Flag::NotSource) != 0) + unsigned long Count = Cache->HeaderP->PackageCount+1; + pkgCache::VerFile **VFList = new pkgCache::VerFile *[Count]; + memset(VFList,0,sizeof(*VFList)*Count); + + // Map versions that we want to write out onto the VerList array. + for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P) + { + if (P->VersionList == 0) continue; - if (I.IsOk() == false) + /* Find the proper version to use. If the policy says there are no + possible selections we return the installed version, if available.. + This prevents dselect from making it obsolete. */ + pkgCache::VerIterator V = CacheFile.GetPolicy()->GetCandidateVer(P); + if (V.end() == true) { - delete [] Buffer; - return _error->Error("Package file %s is out of sync.",I.FileName()); + if (P->CurrentVer == 0) + continue; + V = P.CurrentVer(); } - FileFd PkgF(I.FileName(),FileFd::ReadOnly); - if (_error->PendingError() == true) + pkgCache::VerFileIterator VF = V.FileList(); + for (; VF.end() == false ; ++VF) + if ((VF.File()->Flags & pkgCache::Flag::NotSource) == 0) + break; + + /* Okay, here we have a bit of a problem.. The policy has selected the + currently installed package - however it only exists in the + status file.. We need to write out something or dselect will mark + the package as obsolete! Thus we emit the status file entry, but + below we remove the status line to make it valid for the + available file. However! We only do this if their do exist *any* + non-source versions of the package - that way the dselect obsolete + handling works OK. */ + if (VF.end() == true) { - delete [] Buffer; - return false; + for (pkgCache::VerIterator Cur = P.VersionList(); Cur.end() != true; ++Cur) + { + for (VF = Cur.FileList(); VF.end() == false; ++VF) + { + if ((VF.File()->Flags & pkgCache::Flag::NotSource) == 0) + { + VF = V.FileList(); + break; + } + } + + if (VF.end() == false) + break; + } + } + + VFList[P->ID] = VF; + } + + LocalitySort(VFList,Count,sizeof(*VFList)); + + // Iterate over all the package files and write them out. + char *Buffer = new char[Cache->HeaderP->MaxVerFileSize+10]; + for (pkgCache::VerFile **J = VFList; *J != 0;) + { + pkgCache::PkgFileIterator File(*Cache,(*J)->File + Cache->PkgFileP); + if (File.IsOk() == false) + { + _error->Error(_("Package file %s is out of sync."),File.FileName()); + break; } - /* Write all of the records from this package file, we search the entire - structure to find them */ - for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; P++) + FileFd PkgF(File.FileName(),FileFd::ReadOnly); + if (_error->PendingError() == true) + break; + + /* Write all of the records from this package file, since we + already did locality sorting we can now just seek through the + file in read order. We apply 1 more optimization here, since often + there will be < 1 byte gaps between records (for the \n) we read that + into the next buffer and offset a bit.. */ + unsigned long Pos = 0; + for (; *J != 0; J++) { - for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; V++) + if ((*J)->File + Cache->PkgFileP != File) + break; + + const pkgCache::VerFile &VF = **J; + + // Read the record and then write it out again. + unsigned long Jitter = VF.Offset - Pos; + if (Jitter > 8) { - if (V->FileList == 0) - continue; - if (V.FileList().File() != I) - continue; - - // Read the record and then write it out again. - if (PkgF.Seek(V.FileList()->Offset) == false || - PkgF.Read(Buffer,V.FileList()->Size) == false || - write(STDOUT_FILENO,Buffer,V.FileList()->Size) != V.FileList()->Size) + if (PkgF.Seek(VF.Offset) == false) + break; + Jitter = 0; + } + + if (PkgF.Read(Buffer,VF.Size + Jitter) == false) + break; + Buffer[VF.Size + Jitter] = '\n'; + + // See above.. + if ((File->Flags & pkgCache::Flag::NotSource) == pkgCache::Flag::NotSource) + { + pkgTagSection Tags; + TFRewriteData RW[] = {{"Status",0},{"Config-Version",0},{}}; + const char *Zero = 0; + if (Tags.Scan(Buffer+Jitter,VF.Size+1) == false || + TFRewrite(stdout,Tags,&Zero,RW) == false) { - delete [] Buffer; - return false; - } + _error->Error("Internal Error, Unable to parse a package record"); + break; + } + fputc('\n',stdout); } + else + { + if (fwrite(Buffer+Jitter,VF.Size+1,1,stdout) != 1) + break; + } + + Pos = VF.Offset + VF.Size; } + + fflush(stdout); + if (_error->PendingError() == true) + break; } - return true; + delete [] Buffer; + delete [] VFList; + return !_error->PendingError(); } /*}}}*/ -// DoAdd - Perform an adding operation /*{{{*/ -// --------------------------------------------------------------------- -/* */ -bool DoAdd(CommandLine &CmdL) +// ShowDepends - Helper for printing out a dependency tree /*{{{*/ +bool ShowDepends(CommandLine &CmdL, bool const RevDepends) { - // Make sure there is at least one argument - if (CmdL.FileSize() <= 1) - return _error->Error("You must give at least one file name"); - - // Open the cache - FileFd CacheF(_config->FindDir("Dir::Cache::srcpkgcache"),FileFd::ReadOnly); - if (_error->PendingError() == true) - return false; - - DynamicMMap Map(CacheF,MMap::Public); - if (_error->PendingError() == true) + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL)) return false; - OpTextProgress Progress; - pkgCacheGenerator Gen(Map,Progress); - if (_error->PendingError() == true) - return false; + CacheSetHelperVirtuals helper(false); + APT::VersionList verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::VersionList::CANDIDATE, helper); + if (verset.empty() == true && helper.virtualPkgs.empty() == true) + return _error->Error(_("No packages found")); + std::vector Shown(Cache->Head().PackageCount); - unsigned long Length = CmdL.FileSize() - 1; - for (const char **I = CmdL.FileList + 1; *I != 0; I++) + bool const Recurse = _config->FindB("APT::Cache::RecurseDepends", false); + bool const Installed = _config->FindB("APT::Cache::Installed", false); + bool const Important = _config->FindB("APT::Cache::Important", false); + bool const ShowDepType = _config->FindB("APT::Cache::ShowDependencyType", RevDepends == false); + bool const ShowVersion = _config->FindB("APT::Cache::ShowVersion", false); + bool const ShowPreDepends = _config->FindB("APT::Cache::ShowPre-Depends", true); + bool const ShowDepends = _config->FindB("APT::Cache::ShowDepends", true); + bool const ShowRecommends = _config->FindB("APT::Cache::ShowRecommends", Important == false); + bool const ShowSuggests = _config->FindB("APT::Cache::ShowSuggests", Important == false); + bool const ShowReplaces = _config->FindB("APT::Cache::ShowReplaces", Important == false); + bool const ShowConflicts = _config->FindB("APT::Cache::ShowConflicts", Important == false); + bool const ShowBreaks = _config->FindB("APT::Cache::ShowBreaks", Important == false); + bool const ShowEnhances = _config->FindB("APT::Cache::ShowEnhances", Important == false); + bool const ShowOnlyFirstOr = _config->FindB("APT::Cache::ShowOnlyFirstOr", false); + + while (verset.empty() != true) { - Progress.OverallProgress(I - CmdL.FileList,Length,1,"Generating cache"); - - // Do the merge - FileFd TagF(*I,FileFd::ReadOnly); - debListParser Parser(TagF); - if (_error->PendingError() == true) - return _error->Error("Problem opening %s",*I); - - if (Gen.SelectFile(*I) == false) - return _error->Error("Problem with SelectFile"); - - if (Gen.MergeList(Parser) == false) - return _error->Error("Problem with MergeList"); + pkgCache::VerIterator Ver = *verset.begin(); + verset.erase(verset.begin()); + pkgCache::PkgIterator Pkg = Ver.ParentPkg(); + Shown[Pkg->ID] = true; + + cout << Pkg.FullName(true) << endl; + + if (RevDepends == true) + cout << "Reverse Depends:" << endl; + for (pkgCache::DepIterator D = RevDepends ? Pkg.RevDependsList() : Ver.DependsList(); + D.end() == false; ++D) + { + switch (D->Type) { + case pkgCache::Dep::PreDepends: if (!ShowPreDepends) continue; break; + case pkgCache::Dep::Depends: if (!ShowDepends) continue; break; + case pkgCache::Dep::Recommends: if (!ShowRecommends) continue; break; + case pkgCache::Dep::Suggests: if (!ShowSuggests) continue; break; + case pkgCache::Dep::Replaces: if (!ShowReplaces) continue; break; case pkgCache::Dep::Conflicts: if (!ShowConflicts) continue; break; + case pkgCache::Dep::DpkgBreaks: if (!ShowBreaks) continue; break; + case pkgCache::Dep::Enhances: if (!ShowEnhances) continue; break; + } + + pkgCache::PkgIterator Trg = RevDepends ? D.ParentPkg() : D.TargetPkg(); + + if((Installed && Trg->CurrentVer != 0) || !Installed) + { + + if ((D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or && ShowOnlyFirstOr == false) + cout << " |"; + else + cout << " "; + + // Show the package + if (ShowDepType == true) + cout << D.DepType() << ": "; + if (Trg->VersionList == 0) + cout << "<" << Trg.FullName(true) << ">"; + else + cout << Trg.FullName(true); + if (ShowVersion == true && D->Version != 0) + cout << " (" << pkgCache::CompTypeDeb(D->CompareOp) << ' ' << D.TargetVer() << ')'; + cout << std::endl; + + if (Recurse == true && Shown[Trg->ID] == false) + { + Shown[Trg->ID] = true; + verset.insert(APT::VersionSet::FromPackage(CacheFile, Trg, APT::VersionSet::CANDIDATE, helper)); + } + + } + + // Display all solutions + SPtrArray List = D.AllTargets(); + pkgPrioSortList(*Cache,List); + for (pkgCache::Version **I = List; *I != 0; I++) + { + pkgCache::VerIterator V(*Cache,*I); + if (V != Cache->VerP + V.ParentPkg()->VersionList || + V->ParentPkg == D->Package) + continue; + cout << " " << V.ParentPkg().FullName(true) << endl; + + if (Recurse == true && Shown[V.ParentPkg()->ID] == false) + { + Shown[V.ParentPkg()->ID] = true; + verset.insert(APT::VersionSet::FromPackage(CacheFile, V.ParentPkg(), APT::VersionSet::CANDIDATE, helper)); + } + } + + if (ShowOnlyFirstOr == true) + while ((D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or) ++D; + } } - Progress.Done(); - Stats(Gen.GetCache()); - + for (APT::PackageSet::const_iterator Pkg = helper.virtualPkgs.begin(); + Pkg != helper.virtualPkgs.end(); ++Pkg) + cout << '<' << Pkg.FullName(true) << '>' << endl; + return true; } /*}}}*/ -// GenCaches - Call the main cache generator /*{{{*/ +// Depends - Print out a dependency tree /*{{{*/ // --------------------------------------------------------------------- /* */ -bool GenCaches() +bool Depends(CommandLine &CmdL) { - OpTextProgress Progress; - pkgSourceList List; - List.ReadMainList(); - return pkgMakeStatusCache(List,Progress); + return ShowDepends(CmdL, false); } /*}}}*/ -// ShowHelp - Show a help screen /*{{{*/ +// RDepends - Print out a reverse dependency tree /*{{{*/ // --------------------------------------------------------------------- /* */ -int ShowHelp() +bool RDepends(CommandLine &CmdL) { - cout << PACKAGE << ' ' << VERSION << " for " << ARCHITECTURE << - " compiled on " << __DATE__ << " " << __TIME__ << endl; - - cout << "Usage: apt-cache [options] command" << endl; - cout << " apt-cache [options] add file1 [file1 ...]" << endl; - cout << " apt-cache [options] showpkg pkg2 [pkg2 ...]" << endl; - cout << endl; - cout << "apt-cache is a low-level tool used to manipulate APT's binary" << endl; - cout << "cache files stored in " << _config->FindDir("Dir::Cache") << endl; - cout << "It is not ment for ordinary use only as a debug aide." << endl; - cout << endl; - cout << "Commands:" << endl; - cout << " add - Add an package file to the source cache" << endl; - cout << " gencaches - Build both the package and source cache" << endl; - cout << " showpkg - Show some general information for a single package" << endl; - cout << " stats - Show some basic statistics" << endl; - cout << " dump - Show the entire file in a terse form" << endl; - cout << " dumpavail - Print an available file to stdout" << endl; - cout << endl; - cout << "Options:" << endl; - cout << " -h This help text." << endl; - cout << " -p=? The package cache. [" << _config->FindDir("Dir::Cache::pkgcache") << ']' << endl; - cout << " -s=? The source cache. [" << _config->FindDir("Dir::Cache::srcpkgcache") << ']' << endl; - cout << " -q Disable progress indicator. " << endl; - cout << " -c=? Read this configuration file" << endl; - cout << " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl; - cout << "See the apt-cache(8) and apt.conf(8) manual pages for more information." << endl; - return 100; + return ShowDepends(CmdL, true); } /*}}}*/ +// xvcg - Generate a graph for xvcg /*{{{*/ +// --------------------------------------------------------------------- +// Code contributed from Junichi Uekawa on 20 June 2002. -int main(int argc,const char *argv[]) +bool XVcg(CommandLine &CmdL) { - CommandLine::Args Args[] = { - {'h',"help","help",0}, - {'p',"pkg-cache","Dir::Cache::pkgcache",CommandLine::HasArg}, - {'s',"src-cache","Dir::Cache::srcpkgcache",CommandLine::HasArg}, - {'q',"quiet","quiet",CommandLine::IntLevel}, - {'c',"config-file",0,CommandLine::ConfigFile}, - {'o',"option",0,CommandLine::ArbItem}, - {0,0,0,0}}; + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL)) + return false; + + bool GivenOnly = _config->FindB("APT::Cache::GivenOnly",false); - // Parse the command line and initialize the package library - CommandLine CmdL(Args,_config); - if (pkgInitialize(*_config) == false || - CmdL.Parse(argc,argv) == false) + /* Normal packages are boxes + Pure Provides are triangles + Mixed are diamonds + rhomb are missing packages*/ + const char *Shapes[] = {"ellipse","triangle","box","rhomb"}; + + /* Initialize the list of packages to show. + 1 = To Show + 2 = To Show no recurse + 3 = Emitted no recurse + 4 = Emitted + 0 = None */ + enum States {None=0, ToShow, ToShowNR, DoneNR, Done}; + enum TheFlags {ForceNR=(1<<0)}; + unsigned char *Show = new unsigned char[Cache->Head().PackageCount]; + unsigned char *Flags = new unsigned char[Cache->Head().PackageCount]; + unsigned char *ShapeMap = new unsigned char[Cache->Head().PackageCount]; + + // Show everything if no arguments given + if (CmdL.FileList[1] == 0) + for (unsigned long I = 0; I != Cache->Head().PackageCount; I++) + Show[I] = ToShow; + else + for (unsigned long I = 0; I != Cache->Head().PackageCount; I++) + Show[I] = None; + memset(Flags,0,sizeof(*Flags)*Cache->Head().PackageCount); + + // Map the shapes + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg) + { + if (Pkg->VersionList == 0) + { + // Missing + if (Pkg->ProvidesList == 0) + ShapeMap[Pkg->ID] = 0; + else + ShapeMap[Pkg->ID] = 1; + } + else + { + // Normal + if (Pkg->ProvidesList == 0) + ShapeMap[Pkg->ID] = 2; + else + ShapeMap[Pkg->ID] = 3; + } + } + + // Load the list of packages from the command line into the show list + APT::CacheSetHelper helper(true, GlobalError::NOTICE); + std::list mods; + mods.push_back(APT::PackageSet::Modifier(0, ",", APT::PackageSet::Modifier::POSTFIX)); + mods.push_back(APT::PackageSet::Modifier(1, "^", APT::PackageSet::Modifier::POSTFIX)); + std::map pkgsets = + APT::PackageSet::GroupedFromCommandLine(CacheFile, CmdL.FileList + 1, mods, 0, helper); + + for (APT::PackageSet::const_iterator Pkg = pkgsets[0].begin(); + Pkg != pkgsets[0].end(); ++Pkg) + Show[Pkg->ID] = ToShow; + for (APT::PackageSet::const_iterator Pkg = pkgsets[1].begin(); + Pkg != pkgsets[1].end(); ++Pkg) { - _error->DumpErrors(); - return 100; + Show[Pkg->ID] = ToShow; + Flags[Pkg->ID] |= ForceNR; } - // See if the help should be shown - if (_config->FindB("help") == true || - CmdL.FileSize() == 0) - return ShowHelp(); + // Little header + cout << "graph: { title: \"packages\"" << endl << + "xmax: 700 ymax: 700 x: 30 y: 30" << endl << + "layout_downfactor: 8" << endl; + + bool Act = true; + while (Act == true) + { + Act = false; + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg) + { + // See we need to show this package + if (Show[Pkg->ID] == None || Show[Pkg->ID] >= DoneNR) + continue; + + //printf ("node: { title: \"%s\" label: \"%s\" }\n", Pkg.Name(), Pkg.Name()); + + // Colour as done + if (Show[Pkg->ID] == ToShowNR || (Flags[Pkg->ID] & ForceNR) == ForceNR) + { + // Pure Provides and missing packages have no deps! + if (ShapeMap[Pkg->ID] == 0 || ShapeMap[Pkg->ID] == 1) + Show[Pkg->ID] = Done; + else + Show[Pkg->ID] = DoneNR; + } + else + Show[Pkg->ID] = Done; + Act = true; + + // No deps to map out + if (Pkg->VersionList == 0 || Show[Pkg->ID] == DoneNR) + continue; + + pkgCache::VerIterator Ver = Pkg.VersionList(); + for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; ++D) + { + // See if anything can meet this dep + // Walk along the actual package providing versions + bool Hit = false; + pkgCache::PkgIterator DPkg = D.TargetPkg(); + for (pkgCache::VerIterator I = DPkg.VersionList(); + I.end() == false && Hit == false; ++I) + { + if (Cache->VS->CheckDep(I.VerStr(),D->CompareOp,D.TargetVer()) == true) + Hit = true; + } + + // Follow all provides + for (pkgCache::PrvIterator I = DPkg.ProvidesList(); + I.end() == false && Hit == false; ++I) + { + if (Cache->VS->CheckDep(I.ProvideVersion(),D->CompareOp,D.TargetVer()) == false) + Hit = true; + } + + + // Only graph critical deps + if (D.IsCritical() == true) + { + printf ("edge: { sourcename: \"%s\" targetname: \"%s\" class: 2 ",Pkg.FullName(true).c_str(), D.TargetPkg().FullName(true).c_str() ); + + // Colour the node for recursion + if (Show[D.TargetPkg()->ID] <= DoneNR) + { + /* If a conflicts does not meet anything in the database + then show the relation but do not recurse */ + if (Hit == false && D.IsNegative() == true) + { + if (Show[D.TargetPkg()->ID] == None && + Show[D.TargetPkg()->ID] != ToShow) + Show[D.TargetPkg()->ID] = ToShowNR; + } + else + { + if (GivenOnly == true && Show[D.TargetPkg()->ID] != ToShow) + Show[D.TargetPkg()->ID] = ToShowNR; + else + Show[D.TargetPkg()->ID] = ToShow; + } + } + + // Edge colour + switch(D->Type) + { + case pkgCache::Dep::Conflicts: + printf("label: \"conflicts\" color: lightgreen }\n"); + break; + case pkgCache::Dep::DpkgBreaks: + printf("label: \"breaks\" color: lightgreen }\n"); + break; + case pkgCache::Dep::Obsoletes: + printf("label: \"obsoletes\" color: lightgreen }\n"); + break; + + case pkgCache::Dep::PreDepends: + printf("label: \"predepends\" color: blue }\n"); + break; + + default: + printf("}\n"); + break; + } + } + } + } + } - while (1) + /* Draw the box colours after the fact since we can not tell what colour + they should be until everything is finished drawing */ + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg) { - if (strcmp(CmdL.FileList[0],"add") == 0) + if (Show[Pkg->ID] < DoneNR) + continue; + + if (Show[Pkg->ID] == DoneNR) + printf("node: { title: \"%s\" label: \"%s\" color: orange shape: %s }\n", Pkg.FullName(true).c_str(), Pkg.FullName(true).c_str(), + Shapes[ShapeMap[Pkg->ID]]); + else + printf("node: { title: \"%s\" label: \"%s\" shape: %s }\n", Pkg.FullName(true).c_str(), Pkg.FullName(true).c_str(), + Shapes[ShapeMap[Pkg->ID]]); + + } + + delete[] Show; + delete[] Flags; + delete[] ShapeMap; + + printf("}\n"); + return true; +} + /*}}}*/ +// Dotty - Generate a graph for Dotty /*{{{*/ +// --------------------------------------------------------------------- +/* Dotty is the graphvis program for generating graphs. It is a fairly + simple queuing algorithm that just writes dependencies and nodes. + http://www.research.att.com/sw/tools/graphviz/ */ +bool Dotty(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL)) + return false; + + bool GivenOnly = _config->FindB("APT::Cache::GivenOnly",false); + + /* Normal packages are boxes + Pure Provides are triangles + Mixed are diamonds + Hexagons are missing packages*/ + const char *Shapes[] = {"hexagon","triangle","box","diamond"}; + + /* Initialize the list of packages to show. + 1 = To Show + 2 = To Show no recurse + 3 = Emitted no recurse + 4 = Emitted + 0 = None */ + enum States {None=0, ToShow, ToShowNR, DoneNR, Done}; + enum TheFlags {ForceNR=(1<<0)}; + unsigned char *Show = new unsigned char[Cache->Head().PackageCount]; + unsigned char *Flags = new unsigned char[Cache->Head().PackageCount]; + unsigned char *ShapeMap = new unsigned char[Cache->Head().PackageCount]; + + // Show everything if no arguments given + if (CmdL.FileList[1] == 0) + for (unsigned long I = 0; I != Cache->Head().PackageCount; I++) + Show[I] = ToShow; + else + for (unsigned long I = 0; I != Cache->Head().PackageCount; I++) + Show[I] = None; + memset(Flags,0,sizeof(*Flags)*Cache->Head().PackageCount); + + // Map the shapes + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg) + { + if (Pkg->VersionList == 0) { - DoAdd(CmdL); - break; + // Missing + if (Pkg->ProvidesList == 0) + ShapeMap[Pkg->ID] = 0; + else + ShapeMap[Pkg->ID] = 1; + } + else + { + // Normal + if (Pkg->ProvidesList == 0) + ShapeMap[Pkg->ID] = 2; + else + ShapeMap[Pkg->ID] = 3; } + } + + // Load the list of packages from the command line into the show list + APT::CacheSetHelper helper(true, GlobalError::NOTICE); + std::list mods; + mods.push_back(APT::PackageSet::Modifier(0, ",", APT::PackageSet::Modifier::POSTFIX)); + mods.push_back(APT::PackageSet::Modifier(1, "^", APT::PackageSet::Modifier::POSTFIX)); + std::map pkgsets = + APT::PackageSet::GroupedFromCommandLine(CacheFile, CmdL.FileList + 1, mods, 0, helper); - if (strcmp(CmdL.FileList[0],"gencaches") == 0) + for (APT::PackageSet::const_iterator Pkg = pkgsets[0].begin(); + Pkg != pkgsets[0].end(); ++Pkg) + Show[Pkg->ID] = ToShow; + for (APT::PackageSet::const_iterator Pkg = pkgsets[1].begin(); + Pkg != pkgsets[1].end(); ++Pkg) + { + Show[Pkg->ID] = ToShow; + Flags[Pkg->ID] |= ForceNR; + } + + // Little header + printf("digraph packages {\n"); + printf("concentrate=true;\n"); + printf("size=\"30,40\";\n"); + + bool Act = true; + while (Act == true) + { + Act = false; + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg) { - GenCaches(); - break; + // See we need to show this package + if (Show[Pkg->ID] == None || Show[Pkg->ID] >= DoneNR) + continue; + + // Colour as done + if (Show[Pkg->ID] == ToShowNR || (Flags[Pkg->ID] & ForceNR) == ForceNR) + { + // Pure Provides and missing packages have no deps! + if (ShapeMap[Pkg->ID] == 0 || ShapeMap[Pkg->ID] == 1) + Show[Pkg->ID] = Done; + else + Show[Pkg->ID] = DoneNR; + } + else + Show[Pkg->ID] = Done; + Act = true; + + // No deps to map out + if (Pkg->VersionList == 0 || Show[Pkg->ID] == DoneNR) + continue; + + pkgCache::VerIterator Ver = Pkg.VersionList(); + for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; ++D) + { + // See if anything can meet this dep + // Walk along the actual package providing versions + bool Hit = false; + pkgCache::PkgIterator DPkg = D.TargetPkg(); + for (pkgCache::VerIterator I = DPkg.VersionList(); + I.end() == false && Hit == false; ++I) + { + if (Cache->VS->CheckDep(I.VerStr(),D->CompareOp,D.TargetVer()) == true) + Hit = true; + } + + // Follow all provides + for (pkgCache::PrvIterator I = DPkg.ProvidesList(); + I.end() == false && Hit == false; ++I) + { + if (Cache->VS->CheckDep(I.ProvideVersion(),D->CompareOp,D.TargetVer()) == false) + Hit = true; + } + + // Only graph critical deps + if (D.IsCritical() == true) + { + printf("\"%s\" -> \"%s\"",Pkg.FullName(true).c_str(),D.TargetPkg().FullName(true).c_str()); + + // Colour the node for recursion + if (Show[D.TargetPkg()->ID] <= DoneNR) + { + /* If a conflicts does not meet anything in the database + then show the relation but do not recurse */ + if (Hit == false && D.IsNegative() == true) + { + if (Show[D.TargetPkg()->ID] == None && + Show[D.TargetPkg()->ID] != ToShow) + Show[D.TargetPkg()->ID] = ToShowNR; + } + else + { + if (GivenOnly == true && Show[D.TargetPkg()->ID] != ToShow) + Show[D.TargetPkg()->ID] = ToShowNR; + else + Show[D.TargetPkg()->ID] = ToShow; + } + } + + // Edge colour + switch(D->Type) + { + case pkgCache::Dep::Conflicts: + case pkgCache::Dep::Obsoletes: + case pkgCache::Dep::DpkgBreaks: + printf("[color=springgreen];\n"); + break; + + case pkgCache::Dep::PreDepends: + printf("[color=blue];\n"); + break; + + default: + printf(";\n"); + break; + } + } + } } + } + + /* Draw the box colours after the fact since we can not tell what colour + they should be until everything is finished drawing */ + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg) + { + if (Show[Pkg->ID] < DoneNR) + continue; + + // Orange box for early recursion stoppage + if (Show[Pkg->ID] == DoneNR) + printf("\"%s\" [color=orange,shape=%s];\n",Pkg.FullName(true).c_str(), + Shapes[ShapeMap[Pkg->ID]]); + else + printf("\"%s\" [shape=%s];\n",Pkg.FullName(true).c_str(), + Shapes[ShapeMap[Pkg->ID]]); + } + + printf("}\n"); + delete[] Show; + delete[] Flags; + delete[] ShapeMap; + return true; +} + /*}}}*/ +// DisplayRecord - Displays the complete record for the package /*{{{*/ +// --------------------------------------------------------------------- +/* This displays the package record from the proper package index file. + It is not used by DumpAvail for performance reasons. */ - // Open the cache file - FileFd CacheF(_config->FindDir("Dir::Cache::pkgcache"),FileFd::ReadOnly); - if (_error->PendingError() == true) +static unsigned char const* skipDescriptionFields(unsigned char const * DescP) +{ + char const * const TagName = "\nDescription"; + size_t const TagLen = strlen(TagName); + while ((DescP = (unsigned char*)strchr((char*)DescP, '\n')) != NULL) + { + if (DescP[1] == ' ') + DescP += 2; + else if (strncmp((char*)DescP, TagName, TagLen) == 0) + DescP += TagLen; + else break; - - MMap Map(CacheF,MMap::Public | MMap::ReadOnly); - if (_error->PendingError() == true) + } + if (DescP != NULL) + ++DescP; + return DescP; +} +bool DisplayRecord(pkgCacheFile &CacheFile, pkgCache::VerIterator V) +{ + pkgCache *Cache = CacheFile.GetPkgCache(); + if (unlikely(Cache == NULL)) + return false; + + // Find an appropriate file + pkgCache::VerFileIterator Vf = V.FileList(); + for (; Vf.end() == false; ++Vf) + if ((Vf.File()->Flags & pkgCache::Flag::NotSource) == 0) break; + if (Vf.end() == true) + Vf = V.FileList(); - pkgCache Cache(Map); - if (_error->PendingError() == true) - break; + // Check and load the package list file + pkgCache::PkgFileIterator I = Vf.File(); + if (I.IsOk() == false) + return _error->Error(_("Package file %s is out of sync."),I.FileName()); + + FileFd PkgF; + if (PkgF.Open(I.FileName(), FileFd::ReadOnly, FileFd::Extension) == false) + return false; + + // Read the record (and ensure that it ends with a newline and NUL) + unsigned char *Buffer = new unsigned char[Cache->HeaderP->MaxVerFileSize+2]; + Buffer[Vf->Size] = '\n'; + Buffer[Vf->Size+1] = '\0'; + if (PkgF.Seek(Vf->Offset) == false || + PkgF.Read(Buffer,Vf->Size) == false) + { + delete [] Buffer; + return false; + } + + // Get a pointer to start of Description field + const unsigned char *DescP = (unsigned char*)strstr((char*)Buffer, "\nDescription"); + if (DescP != NULL) + ++DescP; + else + DescP = Buffer + Vf->Size; + + // Write all but Description + size_t const length = DescP - Buffer; + if (length != 0 && FileFd::Write(STDOUT_FILENO, Buffer, length) == false) + { + delete [] Buffer; + return false; + } + + // Show the right description + pkgRecords Recs(*Cache); + pkgCache::DescIterator Desc = V.TranslatedDescription(); + if (Desc.end() == false) + { + pkgRecords::Parser &P = Recs.Lookup(Desc.FileList()); + cout << "Description" << ( (strcmp(Desc.LanguageCode(),"") != 0) ? "-" : "" ) << Desc.LanguageCode() << ": " << P.LongDesc(); + cout << std::endl << "Description-md5: " << Desc.md5() << std::endl; + + // Find the first field after the description (if there is any) + DescP = skipDescriptionFields(DescP); + } + // else we have no translation, so we found a lonely Description-md5 -> don't skip it + + // write the rest of the buffer, but skip mixed in Descriptions* fields + while (DescP != NULL) + { + const unsigned char * const Start = DescP; + const unsigned char *End = (unsigned char*)strstr((char*)DescP, "\nDescription"); + if (End == NULL) + { + End = &Buffer[Vf->Size]; + DescP = NULL; + } + else + { + ++End; // get the newline into the output + DescP = skipDescriptionFields(End + strlen("Description")); + } + size_t const length = End - Start; + if (length != 0 && FileFd::Write(STDOUT_FILENO, Start, length) == false) + { + delete [] Buffer; + return false; + } + } + + // write a final newline after the last field + cout<FindB("APT::Cache::ShowFull",false); + bool const NamesOnly = _config->FindB("APT::Cache::NamesOnly",false); + unsigned int const NumPatterns = CmdL.FileSize() -1; + + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + pkgDepCache::Policy *Plcy = CacheFile.GetPolicy(); + if (unlikely(Cache == NULL || Plcy == NULL)) + return false; + + // Make sure there is at least one argument + if (NumPatterns < 1) + return _error->Error(_("You must give at least one search pattern")); + + // Compile the regex pattern + regex_t *Patterns = new regex_t[NumPatterns]; + memset(Patterns,0,sizeof(*Patterns)*NumPatterns); + for (unsigned I = 0; I != NumPatterns; I++) + { + if (regcomp(&Patterns[I],CmdL.FileList[I+1],REG_EXTENDED | REG_ICASE | + REG_NOSUB) != 0) + { + for (; I != 0; I--) + regfree(&Patterns[I]); + return _error->Error("Regex compilation error"); + } + } + + if (_error->PendingError() == true) + { + for (unsigned I = 0; I != NumPatterns; I++) + regfree(&Patterns[I]); + return false; + } + + size_t const descCount = Cache->HeaderP->GroupCount + 1; + ExDescFile *DFList = new ExDescFile[descCount]; + memset(DFList,0,sizeof(*DFList) * descCount); + + bool PatternMatch[descCount * NumPatterns]; + memset(PatternMatch,false,sizeof(PatternMatch)); + + // Map versions that we want to write out onto the VerList array. + for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G) + { + size_t const PatternOffset = G->ID * NumPatterns; + size_t unmatched = 0, matched = 0; + for (unsigned I = 0; I < NumPatterns; ++I) + { + if (PatternMatch[PatternOffset + I] == true) + ++matched; + else if (regexec(&Patterns[I],G.Name(),0,0,0) == 0) + PatternMatch[PatternOffset + I] = true; + else + ++unmatched; + } + + // already dealt with this package? + if (matched == NumPatterns) + continue; + + // Doing names only, drop any that don't match.. + if (NamesOnly == true && unmatched == NumPatterns) + continue; + + // Find the proper version to use + pkgCache::PkgIterator P = G.FindPreferredPkg(); + if (P.end() == true) + continue; + pkgCache::VerIterator V = Plcy->GetCandidateVer(P); + if (V.end() == false) + { + pkgCache::DescIterator const D = V.TranslatedDescription(); + //FIXME: packages without a description can't be found + if (D.end() == true) + continue; + DFList[G->ID].Df = D.FileList(); + DFList[G->ID].ID = G->ID; + } + + if (unmatched == NumPatterns) + continue; + + // Include all the packages that provide matching names too + for (pkgCache::PrvIterator Prv = P.ProvidesList() ; Prv.end() == false; ++Prv) + { + pkgCache::VerIterator V = Plcy->GetCandidateVer(Prv.OwnerPkg()); + if (V.end() == true) + continue; + + unsigned long id = Prv.OwnerPkg().Group()->ID; + pkgCache::DescIterator const D = V.TranslatedDescription(); + //FIXME: packages without a description can't be found + if (D.end() == true) + continue; + DFList[id].Df = D.FileList(); + DFList[id].ID = id; + + size_t const PrvPatternOffset = id * NumPatterns; + for (unsigned I = 0; I < NumPatterns; ++I) + PatternMatch[PrvPatternOffset + I] = PatternMatch[PatternOffset + I]; + } + } + + LocalitySort(&DFList->Df,Cache->HeaderP->GroupCount,sizeof(*DFList)); + + // Create the text record parser + pkgRecords Recs(*Cache); + // Iterate over all the version records and check them + for (ExDescFile *J = DFList; J->Df != 0; ++J) + { + pkgRecords::Parser &P = Recs.Lookup(pkgCache::DescFileIterator(*Cache,J->Df)); + size_t const PatternOffset = J->ID * NumPatterns; + + if (NamesOnly == false) + { + string const LongDesc = P.LongDesc(); + for (unsigned I = 0; I < NumPatterns; ++I) + { + if (PatternMatch[PatternOffset + I] == true) + continue; + else if (regexec(&Patterns[I],LongDesc.c_str(),0,0,0) == 0) + PatternMatch[PatternOffset + I] = true; + } + } + + bool matchedAll = true; + for (unsigned I = 0; I < NumPatterns; ++I) + if (PatternMatch[PatternOffset + I] == false) + { + matchedAll = false; + break; + } + + if (matchedAll == true) + { + if (ShowFull == true) + { + const char *Start; + const char *End; + P.GetRec(Start,End); + fwrite(Start,End-Start,1,stdout); + putc('\n',stdout); + } + else + printf("%s - %s\n",P.Name().c_str(),P.ShortDesc().c_str()); + } + } + + delete [] DFList; + for (unsigned I = 0; I != NumPatterns; I++) + regfree(&Patterns[I]); + if (ferror(stdout)) + return _error->Error("Write to stdout failed"); + return true; +} + /*}}}*/ +/* ShowAuto - show automatically installed packages (sorted) {{{*/ +bool ShowAuto(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + pkgDepCache *DepCache = CacheFile.GetDepCache(); + if (unlikely(Cache == NULL || DepCache == NULL)) + return false; + + std::vector packages; + packages.reserve(Cache->HeaderP->PackageCount / 3); + + for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P) + if ((*DepCache)[P].Flags & pkgCache::Flag::Auto) + packages.push_back(P.Name()); + + std::sort(packages.begin(), packages.end()); - if (strcmp(CmdL.FileList[0],"showpkg") == 0) + for (vector::iterator I = packages.begin(); I != packages.end(); ++I) + cout << *I << "\n"; + + _error->Notice(_("This command is deprecated. Please use 'apt-mark showauto' instead.")); + return true; +} + /*}}}*/ +// ShowPackage - Dump the package record to the screen /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool ShowPackage(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + CacheSetHelperVirtuals helper(true, GlobalError::NOTICE); + APT::VersionList::Version const select = _config->FindB("APT::Cache::AllVersions", true) ? + APT::VersionList::ALL : APT::VersionList::CANDIDATE; + APT::VersionList const verset = APT::VersionList::FromCommandLine(CacheFile, CmdL.FileList + 1, select, helper); + for (APT::VersionList::const_iterator Ver = verset.begin(); Ver != verset.end(); ++Ver) + if (DisplayRecord(CacheFile, Ver) == false) + return false; + + if (verset.empty() == true) + { + if (helper.virtualPkgs.empty() == true) + return _error->Error(_("No packages found")); + else + _error->Notice(_("No packages found")); + } + return true; +} + /*}}}*/ +// ShowPkgNames - Show package names /*{{{*/ +// --------------------------------------------------------------------- +/* This does a prefix match on the first argument */ +bool ShowPkgNames(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + if (unlikely(CacheFile.BuildCaches(NULL, false) == false)) + return false; + pkgCache::GrpIterator I = CacheFile.GetPkgCache()->GrpBegin(); + bool const All = _config->FindB("APT::Cache::AllNames","false"); + + if (CmdL.FileList[1] != 0) + { + for (;I.end() != true; ++I) { - DumpPackage(Cache,CmdL); - break; + if (All == false && I->FirstPackage == 0) + continue; + if (I.FindPkg("any")->VersionList == 0) + continue; + if (strncmp(I.Name(),CmdL.FileList[1],strlen(CmdL.FileList[1])) == 0) + cout << I.Name() << endl; } - if (strcmp(CmdL.FileList[0],"stats") == 0) + return true; + } + + // Show all pkgs + for (;I.end() != true; ++I) + { + if (All == false && I->FirstPackage == 0) + continue; + if (I.FindPkg("any")->VersionList == 0) + continue; + cout << I.Name() << endl; + } + + return true; +} + /*}}}*/ +// ShowSrcPackage - Show source package records /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool ShowSrcPackage(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + pkgSourceList *List = CacheFile.GetSourceList(); + if (unlikely(List == NULL)) + return false; + + // Create the text record parsers + pkgSrcRecords SrcRecs(*List); + if (_error->PendingError() == true) + return false; + + unsigned found = 0; + for (const char **I = CmdL.FileList + 1; *I != 0; I++) + { + SrcRecs.Restart(); + + pkgSrcRecords::Parser *Parse; + unsigned found_this = 0; + while ((Parse = SrcRecs.Find(*I,false)) != 0) { + cout << Parse->AsStr() << endl;; + found++; + found_this++; + } + if (found_this == 0) { + _error->Warning(_("Unable to locate package %s"),*I); + continue; + } + } + if (found == 0) + _error->Notice(_("No packages found")); + return true; +} + /*}}}*/ +// Policy - Show the results of the preferences file /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool Policy(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + pkgCache *Cache = CacheFile.GetPkgCache(); + pkgPolicy *Plcy = CacheFile.GetPolicy(); + pkgSourceList *SrcList = CacheFile.GetSourceList(); + if (unlikely(Cache == NULL || Plcy == NULL || SrcList == NULL)) + return false; + + /* Should the MultiArchKiller be run to see which pseudo packages for an + arch all package are currently installed? Activating it gives a speed + penality for no real gain beside enhanced debugging, so in general no. */ + if (_config->FindB("APT::Cache::Policy::DepCache", false) == true) + CacheFile.GetDepCache(); + + // Print out all of the package files + if (CmdL.FileList[1] == 0) + { + cout << _("Package files:") << endl; + for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F.end() == false; ++F) { - Stats(Cache); - break; + // Locate the associated index files so we can derive a description + pkgIndexFile *Indx; + if (SrcList->FindIndex(F,Indx) == false && + _system->FindIndex(F,Indx) == false) + return _error->Error(_("Cache is out of sync, can't x-ref a package file")); + + printf("%4i %s\n", + Plcy->GetPriority(F),Indx->Describe(true).c_str()); + + // Print the reference information for the package + string Str = F.RelStr(); + if (Str.empty() == false) + printf(" release %s\n",F.RelStr().c_str()); + if (F.Site() != 0 && F.Site()[0] != 0) + printf(" origin %s\n",F.Site()); } - if (strcmp(CmdL.FileList[0],"dump") == 0) + // Show any packages have explicit pins + cout << _("Pinned packages:") << endl; + pkgCache::PkgIterator I = Cache->PkgBegin(); + for (;I.end() != true; ++I) { - Dump(Cache); - break; + if (Plcy->GetPriority(I) == 0) + continue; + + // Print the package name and the version we are forcing to + cout << " " << I.FullName(true) << " -> "; + + pkgCache::VerIterator V = Plcy->GetMatch(I); + if (V.end() == true) + cout << _("(not found)") << endl; + else + cout << V.VerStr() << endl; + } + + return true; + } + + char const * const msgInstalled = _(" Installed: "); + char const * const msgCandidate = _(" Candidate: "); + short const InstalledLessCandidate = + mbstowcs(NULL, msgInstalled, 0) - mbstowcs(NULL, msgCandidate, 0); + short const deepInstalled = + (InstalledLessCandidate < 0 ? (InstalledLessCandidate*-1) : 0) - 1; + short const deepCandidate = + (InstalledLessCandidate > 0 ? (InstalledLessCandidate) : 0) - 1; + + // Print out detailed information for each package + APT::CacheSetHelper helper(true, GlobalError::NOTICE); + APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1, helper); + for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) + { + cout << Pkg.FullName(true) << ":" << endl; + + // Installed version + cout << msgInstalled << OutputInDepth(deepInstalled, " "); + if (Pkg->CurrentVer == 0) + cout << _("(none)") << endl; + else + cout << Pkg.CurrentVer().VerStr() << endl; + + // Candidate Version + cout << msgCandidate << OutputInDepth(deepCandidate, " "); + pkgCache::VerIterator V = Plcy->GetCandidateVer(Pkg); + if (V.end() == true) + cout << _("(none)") << endl; + else + cout << V.VerStr() << endl; + + // Pinned version + if (Plcy->GetPriority(Pkg) != 0) + { + cout << _(" Package pin: "); + V = Plcy->GetMatch(Pkg); + if (V.end() == true) + cout << _("(not found)") << endl; + else + cout << V.VerStr() << endl; } - if (strcmp(CmdL.FileList[0],"dumpavail") == 0) + // Show the priority tables + cout << _(" Version table:") << endl; + for (V = Pkg.VersionList(); V.end() == false; ++V) { - DumpAvail(Cache); - break; + if (Pkg.CurrentVer() == V) + cout << " *** " << V.VerStr(); + else + cout << " " << V.VerStr(); + cout << " " << Plcy->GetPriority(Pkg) << endl; + for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF) + { + // Locate the associated index files so we can derive a description + pkgIndexFile *Indx; + if (SrcList->FindIndex(VF.File(),Indx) == false && + _system->FindIndex(VF.File(),Indx) == false) + return _error->Error(_("Cache is out of sync, can't x-ref a package file")); + printf(" %4i %s\n",Plcy->GetPriority(VF.File()), + Indx->Describe(true).c_str()); + } } - - _error->Error("Invalid operation %s", CmdL.FileList[0]); - break; } - // Print any errors or warnings found during parsing - if (_error->empty() == false) + return true; +} + /*}}}*/ +// Madison - Look a bit like katie's madison /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool Madison(CommandLine &CmdL) +{ + pkgCacheFile CacheFile; + pkgSourceList *SrcList = CacheFile.GetSourceList(); + + if (SrcList == 0) + return false; + + // Create the src text record parsers and ignore errors about missing + // deb-src lines that are generated from pkgSrcRecords::pkgSrcRecords + pkgSrcRecords SrcRecs(*SrcList); + if (_error->PendingError() == true) + _error->Discard(); + + APT::CacheSetHelper helper(true, GlobalError::NOTICE); + for (const char **I = CmdL.FileList + 1; *I != 0; I++) + { + _error->PushToStack(); + APT::PackageList pkgset = APT::PackageList::FromString(CacheFile, *I, helper); + for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) + { + for (pkgCache::VerIterator V = Pkg.VersionList(); V.end() == false; ++V) + { + for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF) + { +// This might be nice, but wouldn't uniquely identify the source -mdz +// if (VF.File().Archive() != 0) +// { +// cout << setw(10) << Pkg.Name() << " | " << setw(10) << V.VerStr() << " | " +// << VF.File().Archive() << endl; +// } + + // Locate the associated index files so we can derive a description + for (pkgSourceList::const_iterator S = SrcList->begin(); S != SrcList->end(); ++S) + { + vector *Indexes = (*S)->GetIndexFiles(); + for (vector::const_iterator IF = Indexes->begin(); + IF != Indexes->end(); ++IF) + { + if ((*IF)->FindInCache(*(VF.File().Cache())) == VF.File()) + { + cout << setw(10) << Pkg.FullName(true) << " | " << setw(10) << V.VerStr() << " | " + << (*IF)->Describe(true) << endl; + } + } + } + } + } + } + + SrcRecs.Restart(); + pkgSrcRecords::Parser *SrcParser; + bool foundSomething = false; + while ((SrcParser = SrcRecs.Find(*I, false)) != 0) + { + foundSomething = true; + // Maybe support Release info here too eventually + cout << setw(10) << SrcParser->Package() << " | " + << setw(10) << SrcParser->Version() << " | " + << SrcParser->Index().Describe(true) << endl; + } + if (foundSomething == true) + _error->RevertToStack(); + else + _error->MergeWithStack(); + } + + return true; +} + /*}}}*/ +// GenCaches - Call the main cache generator /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool GenCaches(CommandLine &Cmd) +{ + OpTextProgress Progress(*_config); + + pkgCacheFile CacheFile; + return CacheFile.BuildCaches(&Progress, true); +} + /*}}}*/ +// ShowHelp - Show a help screen /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool ShowHelp(CommandLine &Cmd) +{ + ioprintf(cout,_("%s %s for %s compiled on %s %s\n"),PACKAGE,PACKAGE_VERSION, + COMMON_ARCH,__DATE__,__TIME__); + + if (_config->FindB("version") == true) + return true; + + cout << + _("Usage: apt-cache [options] command\n" + " apt-cache [options] showpkg pkg1 [pkg2 ...]\n" + " apt-cache [options] showsrc pkg1 [pkg2 ...]\n" + "\n" + "apt-cache is a low-level tool used to query information\n" + "from APT's binary cache files\n" + "\n" + "Commands:\n" + " gencaches - Build both the package and source cache\n" + " showpkg - Show some general information for a single package\n" + " showsrc - Show source records\n" + " stats - Show some basic statistics\n" + " dump - Show the entire file in a terse form\n" + " dumpavail - Print an available file to stdout\n" + " unmet - Show unmet dependencies\n" + " search - Search the package list for a regex pattern\n" + " show - Show a readable record for the package\n" + " depends - Show raw dependency information for a package\n" + " rdepends - Show reverse dependency information for a package\n" + " pkgnames - List the names of all packages in the system\n" + " dotty - Generate package graphs for GraphViz\n" + " xvcg - Generate package graphs for xvcg\n" + " policy - Show policy settings\n" + "\n" + "Options:\n" + " -h This help text.\n" + " -p=? The package cache.\n" + " -s=? The source cache.\n" + " -q Disable progress indicator.\n" + " -i Show only important deps for the unmet command.\n" + " -c=? Read this configuration file\n" + " -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" + "See the apt-cache(8) and apt.conf(5) manual pages for more information.\n"); + return true; +} + /*}}}*/ +int main(int argc,const char *argv[]) /*{{{*/ +{ + CommandLine::Dispatch Cmds[] = {{"help",&ShowHelp}, + {"gencaches",&GenCaches}, + {"showsrc",&ShowSrcPackage}, + {"showpkg",&DumpPackage}, + {"stats",&Stats}, + {"dump",&Dump}, + {"dumpavail",&DumpAvail}, + {"unmet",&UnMet}, + {"search",&Search}, + {"depends",&Depends}, + {"rdepends",&RDepends}, + {"dotty",&Dotty}, + {"xvcg",&XVcg}, + {"show",&ShowPackage}, + {"pkgnames",&ShowPkgNames}, + {"showauto",&ShowAuto}, + {"policy",&Policy}, + {"madison",&Madison}, + {0,0}}; + + std::vector Args = getCommandArgs("apt-cache", CommandLine::GetCommand(Cmds, argc, argv)); + + // Set up gettext support + setlocale(LC_ALL,""); + textdomain(PACKAGE); + + // Parse the command line and initialize the package library + CommandLine CmdL(Args.data(),_config); + if (pkgInitConfig(*_config) == false || + CmdL.Parse(argc,argv) == false || + pkgInitSystem(*_config,_system) == false) { _error->DumpErrors(); return 100; } - - return 0; + + // See if the help should be shown + if (_config->FindB("help") == true || + CmdL.FileSize() == 0) + { + ShowHelp(CmdL); + return 0; + } + + // Deal with stdout not being a tty + if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1) + _config->Set("quiet","1"); + + if (_config->Exists("APT::Cache::Generate") == true) + _config->Set("pkgCacheFile::Generate", _config->FindB("APT::Cache::Generate", true)); + + // Match the operation + CmdL.DispatchArg(Cmds); + + // Print any errors or warnings found during parsing + bool const Errors = _error->PendingError(); + if (_config->FindI("quiet",0) > 0) + _error->DumpErrors(); + else + _error->DumpErrors(GlobalError::DEBUG); + return Errors == true ? 100 : 0; } + /*}}}*/