From 6c139d6e362f04a1582e8a8f511f8aeab031fecf Mon Sep 17 00:00:00 2001 From: Arch Librarian Date: Mon, 20 Sep 2004 16:50:41 +0000 Subject: Sync Author: jgg Date: 1998-07-07 04:17:00 GMT Sync --- apt-pkg/algorithms.cc | 732 +++++++++++++++++++++++++++++++ apt-pkg/algorithms.h | 91 ++++ apt-pkg/cacheiterators.h | 21 +- apt-pkg/contrib/configuration.cc | 137 ++++++ apt-pkg/contrib/configuration.h | 61 +++ apt-pkg/contrib/error.cc | 74 +++- apt-pkg/contrib/error.h | 15 +- apt-pkg/contrib/fileutl.cc | 5 +- apt-pkg/contrib/fileutl.h | 6 +- apt-pkg/contrib/mmap.cc | 6 +- apt-pkg/contrib/mmap.h | 6 +- apt-pkg/contrib/strutl.cc | 343 +++++++++++++++ apt-pkg/contrib/strutl.h | 36 ++ apt-pkg/deb/deblistparser.cc | 90 ++-- apt-pkg/depcache.cc | 903 +++++++++++++++++++++++++++++++++++++++ apt-pkg/depcache.h | 189 ++++++++ apt-pkg/orderlist.cc | 829 +++++++++++++++++++++++++++++++++++ apt-pkg/orderlist.h | 123 ++++++ apt-pkg/packagemanager.cc | 548 ++++++++++++++++++++++++ apt-pkg/packagemanager.h | 84 ++++ apt-pkg/pkgcache.cc | 24 +- apt-pkg/pkgcache.h | 42 +- apt-pkg/pkgcachegen.cc | 6 +- apt-pkg/pkgcachegen.h | 6 +- apt-pkg/sourcelist.cc | 465 ++++++++++++++++++++ apt-pkg/sourcelist.h | 78 ++++ apt-pkg/tagfile.cc | 6 +- apt-pkg/tagfile.h | 6 +- apt-pkg/version.cc | 18 +- apt-pkg/version.h | 6 +- 30 files changed, 4841 insertions(+), 115 deletions(-) create mode 100644 apt-pkg/algorithms.cc create mode 100644 apt-pkg/algorithms.h create mode 100644 apt-pkg/contrib/configuration.cc create mode 100644 apt-pkg/contrib/configuration.h create mode 100644 apt-pkg/contrib/strutl.cc create mode 100644 apt-pkg/contrib/strutl.h create mode 100644 apt-pkg/depcache.cc create mode 100644 apt-pkg/depcache.h create mode 100644 apt-pkg/orderlist.cc create mode 100644 apt-pkg/orderlist.h create mode 100644 apt-pkg/packagemanager.cc create mode 100644 apt-pkg/packagemanager.h create mode 100644 apt-pkg/sourcelist.cc create mode 100644 apt-pkg/sourcelist.h diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc new file mode 100644 index 000000000..e3012489d --- /dev/null +++ b/apt-pkg/algorithms.cc @@ -0,0 +1,732 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: algorithms.cc,v 1.1 1998/07/07 04:17:00 jgg Exp $ +/* ###################################################################### + + Algorithms - A set of misc algorithms + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/algorithms.h" +#endif +#include +#include +#include +#include + /*}}}*/ + +pkgProblemResolver *pkgProblemResolver::This = 0; + +// Simulate::Simulate - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgSimulate::pkgSimulate(pkgDepCache &Cache) : pkgPackageManager(Cache), + Sim(true,true) +{ + Flags = new unsigned char[Cache.HeaderP->PackageCount]; + memset(Flags,0,sizeof(*Flags)*Cache.HeaderP->PackageCount); +} + /*}}}*/ +// Simulate::Install - Simulate unpacking of a package /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/) +{ + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name()); + Flags[Pkg->ID] = 1; + + cout << "Inst " << Pkg.Name(); + Sim.MarkInstall(Pkg,false); + + // Look for broken conflicts+predepends. + for (PkgIterator I = Sim.PkgBegin(); I.end() == false; I++) + { + if (Sim[I].InstallVer == 0) + continue; + + for (DepIterator D = Sim[I].InstVerIter(Sim).DependsList(); D.end() == false; D++) + if (D->Type == pkgDEP_Conflicts || D->Type == pkgDEP_PreDepends) + { + if ((Sim[D] & pkgDepCache::DepInstall) == 0) + { + cout << " [" << I.Name() << " on " << D.TargetPkg().Name() << ']'; + if (D->Type == pkgDEP_Conflicts) + _error->Error("Fatal, conflicts violated %s",I.Name()); + } + } + } + + if (Sim.BrokenCount() != 0) + ShortBreaks(); + else + cout << endl; + return true; +} + /*}}}*/ +// Simulate::Configure - Simulate configuration of a Package /*{{{*/ +// --------------------------------------------------------------------- +/* This is not an acurate simulation of relatity, we should really not + install the package.. For some investigations it may be necessary + however. */ +bool pkgSimulate::Configure(PkgIterator iPkg) +{ + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name()); + + Flags[Pkg->ID] = 2; +// Sim.MarkInstall(Pkg,false); + if (Sim[Pkg].InstBroken() == true) + { + cout << "Conf " << Pkg.Name() << " broken" << endl; + + Sim.Update(); + + // Print out each package and the failed dependencies + for (pkgCache::DepIterator D = Sim[Pkg].InstVerIter(Sim).DependsList(); D.end() == false; D++) + { + if (Sim.IsImportantDep(D) == false || + (Sim[D] & pkgDepCache::DepInstall) != 0) + continue; + + if (D->Type == pkgDEP_Conflicts) + cout << " Conflicts:" << D.TargetPkg().Name(); + else + cout << " Depends:" << D.TargetPkg().Name(); + } + cout << endl; + + _error->Error("Conf Broken %s",Pkg.Name()); + } + else + cout << "Conf " << Pkg.Name(); + + if (Sim.BrokenCount() != 0) + ShortBreaks(); + else + cout << endl; + + return true; +} + /*}}}*/ +// Simulate::Remove - Simulate the removal of a package /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSimulate::Remove(PkgIterator iPkg) +{ + // Adapt the iterator + PkgIterator Pkg = Sim.FindPkg(iPkg.Name()); + + Flags[Pkg->ID] = 3; + Sim.MarkDelete(Pkg); + cout << "Remv " << Pkg.Name(); + + if (Sim.BrokenCount() != 0) + ShortBreaks(); + else + cout << endl; + + return true; +} + /*}}}*/ +// Simulate::ShortBreaks - Print out a short line describing all breaks /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgSimulate::ShortBreaks() +{ + cout << " ["; + for (PkgIterator I = Sim.PkgBegin(); I.end() == false; I++) + { + if (Sim[I].InstBroken() == true) + { + if (Flags[I->ID] == 0) + cout << I.Name() << ' '; +/* else + cout << I.Name() << "! ";*/ + } + } + cout << ']' << endl; +} + /*}}}*/ +// ApplyStatus - Adjust for non-ok packages /*{{{*/ +// --------------------------------------------------------------------- +/* We attempt to change the state of the all packages that have failed + installation toward their real state. The ordering code will perform + the necessary calculations to deal with the problems. */ +bool pkgApplyStatus(pkgDepCache &Cache) +{ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + switch (I->CurrentState) + { + // This means installation failed somehow + case pkgSTATE_UnPacked: + case pkgSTATE_HalfConfigured: + Cache.MarkKeep(I); + break; + + // This means removal failed + case pkgSTATE_HalfInstalled: + Cache.MarkDelete(I); + break; + + default: + if (I->InstState != pkgSTATE_Ok) + return _error->Error("The package %s is not ok and I " + "don't know how to fix it!",I.Name()); + } + } + return true; +} + /*}}}*/ +// FixBroken - Fix broken packages /*{{{*/ +// --------------------------------------------------------------------- +/* This autoinstalls every broken package and then runs ScoredFix on the + result. */ +bool pkgFixBroken(pkgDepCache &Cache) +{ + // Auto upgrade all broken packages + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + if (Cache[I].NowBroken() == true) + Cache.MarkInstall(I,true); + + /* Fix packages that are in a NeedArchive state but don't have a + downloadable install version */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + if (I.State() != pkgCache::PkgIterator::NeedsUnpack || + Cache[I].Delete() == true) + continue; + + if ((Cache[I].InstVerIter(Cache).File()->Flags & pkgFLAG_NotSource) == 0) + continue; + + Cache.MarkInstall(I,true); + } + + pkgProblemResolver Fix(Cache); + return Fix.Resolve(true); +} + /*}}}*/ +// DistUpgrade - Distribution upgrade /*{{{*/ +// --------------------------------------------------------------------- +/* This autoinstalls every package and then force installs every + pre-existing package. This creates the initial set of conditions which + most likely contain problems because too many things were installed. + + ScoredFix is used to resolve the problems. + */ +bool pkgDistUpgrade(pkgDepCache &Cache) +{ + /* Auto upgrade all installed packages, this provides the basis + for the installation */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + if (I->CurrentVer != 0) + Cache.MarkInstall(I,true); + + /* Now, auto upgrade all essential packages - this ensures that + the essential packages are present and working */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + if ((I->Flags & pkgFLAG_Essential) == pkgFLAG_Essential) + Cache.MarkInstall(I,true); + + /* We do it again over all previously installed packages to force + conflict resolution on them all. */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + if (I->CurrentVer != 0) + Cache.MarkInstall(I,false); + + pkgProblemResolver Fix(Cache); + + // Hold back held packages. + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + if (I->SelectedState == pkgSTATE_Hold) + { + Fix.Protect(I); + Cache.MarkKeep(I); + } + } + + return Fix.Resolve(); +} + /*}}}*/ + +// ProblemResolver::pkgProblemResolver - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgProblemResolver::pkgProblemResolver(pkgDepCache &Cache) : Cache(Cache) +{ + // Allocate memory + unsigned long Size = Cache.HeaderP->PackageCount; + Scores = new signed short[Size]; + Flags = new unsigned char[Size]; + memset(Flags,0,sizeof(*Flags)*Size); + + // Set debug to true to see its decision logic + Debug = false; +} + /*}}}*/ +// ProblemResolver::ScoreSort - Sort the list by score /*{{{*/ +// --------------------------------------------------------------------- +/* */ +int pkgProblemResolver::ScoreSort(const void *a,const void *b) +{ + Package const **A = (Package const **)a; + Package const **B = (Package const **)b; + if (This->Scores[(*A)->ID] > This->Scores[(*B)->ID]) + return -1; + if (This->Scores[(*A)->ID] < This->Scores[(*B)->ID]) + return 1; + return 0; +} + /*}}}*/ +// ProblemResolver::MakeScores - Make the score table /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgProblemResolver::MakeScores() +{ + unsigned long Size = Cache.HeaderP->PackageCount; + memset(Scores,0,sizeof(*Scores)*Size); + + // Generate the base scores for a package based on its properties + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + if (Cache[I].InstallVer == 0) + continue; + + signed short &Score = Scores[I->ID]; + + /* This is arbitary, it should be high enough to elevate an + essantial package above most other packages but low enough + to allow an obsolete essential packages to be removed by + a conflicts on a powerfull normal package (ie libc6) */ + if ((I->Flags & pkgFLAG_Essential) == pkgFLAG_Essential) + Score += 100; + + // We transform the priority + // Important Required Standard Optional Extra + signed short PrioMap[] = {0,3,2,1,-1,-2}; + if (Cache[I].InstVerIter(Cache)->Priority <= 5) + Score += PrioMap[Cache[I].InstVerIter(Cache)->Priority]; + + /* This helps to fix oddball problems with conflicting packages + on the same level. We enhance the score of installed packages */ + if (I->CurrentVer != 0) + Score += 1; + } + + // Now that we have the base scores we go and propogate dependencies + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + if (Cache[I].InstallVer == 0) + continue; + + for (pkgCache::DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); D.end() == false; D++) + { + if (D->Type == pkgDEP_Depends || D->Type == pkgDEP_PreDepends) + Scores[D.TargetPkg()->ID]++; + } + } + + // Copy the scores to advoid additive looping + signed short *OldScores = new signed short[Size]; + memcpy(OldScores,Scores,sizeof(*Scores)*Size); + + /* Now we cause 1 level of dependency inheritance, that is we add the + score of the packages that depend on the target Package. This + fortifies high scoring packages */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + if (Cache[I].InstallVer == 0) + continue; + + for (pkgCache::DepIterator D = I.RevDependsList(); D.end() == false; D++) + { + // Only do it for the install version + if ((pkgCache::Version *)D.ParentVer() != Cache[D.ParentPkg()].InstallVer || + (D->Type != pkgDEP_Depends && D->Type != pkgDEP_PreDepends)) + continue; + + Scores[I->ID] += abs(OldScores[D.ParentPkg()->ID]); + } + } + + /* Now we propogate along provides. This makes the packages that + provide important packages extremely important */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + for (pkgCache::PrvIterator P = I.ProvidesList(); P.end() == false; P++) + { + // Only do it once per package + if ((pkgCache::Version *)P.OwnerVer() != Cache[P.OwnerPkg()].InstallVer) + continue; + Scores[P.OwnerPkg()->ID] += abs(Scores[I->ID] - OldScores[I->ID]); + } + } + + /* Protected things are pushed really high up. This number should put them + ahead of everything */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + if ((Flags[I->ID] & Protected) != 0) + Scores[I->ID] += 10000; + + delete [] OldScores; +} + /*}}}*/ +// ProblemResolver::DoUpgrade - Attempt to upgrade this package /*{{{*/ +// --------------------------------------------------------------------- +/* This goes through and tries to reinstall packages to make this package + installable */ +bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) +{ + if ((Flags[Pkg->ID] & Upgradable) == 0 || Cache[Pkg].Upgradable() == false) + return false; + Flags[Pkg->ID] &= ~Upgradable; + + bool WasKept = Cache[Pkg].Keep(); + Cache.MarkInstall(Pkg,false); + + // Isolate the problem dependency + bool Fail = false; + for (pkgCache::DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); D.end() == false;) + { + // Compute a single dependency element (glob or) + pkgCache::DepIterator Start = D; + pkgCache::DepIterator End = D; + unsigned char State = 0; + for (bool LastOR = true; D.end() == false && LastOR == true; D++) + { + State |= Cache[D]; + LastOR = (D->CompareOp & pkgOP_OR) == pkgOP_OR; + if (LastOR == true) + End = D; + } + + // We only worry about critical deps. + if (End.IsCritical() != true) + continue; + + // Dep is ok + if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) + continue; + + // Hm, the group is broken.. I have no idea how to handle this + if (Start != End) + { + cout << "Note, a broken or group was found in " << Pkg.Name() << "." << endl; + Fail = true; + break; + } + + // Upgrade the package if the candidate version will fix the problem. + if ((Cache[Start] & pkgDepCache::DepCVer) == pkgDepCache::DepCVer) + { + PkgIterator P = Start.SmartTargetPkg(); + if (DoUpgrade(P) == false) + { + if (Debug == true) + cout << " Reinst Failed because of " << P.Name() << endl; + Fail = true; + break; + } + } + else + { + /* We let the algorithm deal with conflicts on its next iteration, + it is much smarter than us */ + if (End->Type == pkgDEP_Conflicts) + continue; + + if (Debug == true) + cout << " Reinst Failed early because of " << Start.TargetPkg().Name() << endl; + Fail = true; + break; + } + } + + // Undo our operations - it might be smart to undo everything this did.. + if (Fail == true) + { + if (WasKept == true) + Cache.MarkKeep(Pkg); + else + Cache.MarkDelete(Pkg); + return false; + } + + if (Debug == true) + cout << " Re-Instated " << Pkg.Name() << endl; + return true; +} + /*}}}*/ +// ProblemResolver::Resolve - Run the resolution pass /*{{{*/ +// --------------------------------------------------------------------- +/* This routines works by calculating a score for each package. The score + is derived by considering the package's priority and all reverse + dependents giving an integer that reflects the amount of breakage that + adjusting the package will inflict. + + It goes from highest score to lowest and corrects all of the breaks by + keeping or removing the dependant packages. If that fails then it removes + the package itself and goes on. The routine should be able to intelligently + go from any broken state to a fixed state. + + The BrokenFix flag enables a mode where the algorithm tries to + upgrade packages to advoid problems. */ +bool pkgProblemResolver::Resolve(bool BrokenFix) +{ + unsigned long Size = Cache.HeaderP->PackageCount; + + // Record which packages are marked for install + bool Again = false; + do + { + Again = false; + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + if (Cache[I].Install() == true) + Flags[I->ID] |= PreInstalled; + else + { + if (Cache[I].InstBroken() == true && BrokenFix == true) + { + Cache.MarkInstall(I,false); + if (Cache[I].Install() == true) + Again = true; + } + + Flags[I->ID] &= ~PreInstalled; + } + Flags[I->ID] |= Upgradable; + } + } + while (Again == true); + + if (Debug == true) + cout << "Starting" << endl; + + MakeScores(); + + /* We have to order the packages so that the broken fixing pass + operates from highest score to lowest. This prevents problems when + high score packages cause the removal of lower score packages that + would cause the removal of even lower score packages. */ + pkgCache::Package **PList = new pkgCache::Package *[Size]; + pkgCache::Package **PEnd = PList; + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + *PEnd++ = I; + This = this; + qsort(PList,PEnd - PList,sizeof(*PList),&ScoreSort); + +/* for (pkgCache::Package **K = PList; K != PEnd; K++) + if (Scores[(*K)->ID] != 0) + { + pkgCache::PkgIterator Pkg(Cache,*K); + cout << Scores[(*K)->ID] << ' ' << Pkg.Name() << + ' ' << (pkgCache::Version *)Pkg.CurrentVer() << ' ' << + Cache[Pkg].InstallVer << ' ' << Cache[Pkg].CandidateVer << endl; + } */ + + if (Debug == true) + cout << "Starting 2" << endl; + + /* Now consider all broken packages. For each broken package we either + remove the package or fix it's problem. We do this once, it should + not be possible for a loop to form (that is a < b < c and fixing b by + changing a breaks c) */ + bool Change = true; + for (int Counter = 0; Counter != 10 && Change == true; Counter++) + { + Change = false; + for (pkgCache::Package **K = PList; K != PEnd; K++) + { + pkgCache::PkgIterator I(Cache,*K); + + /* We attempt to install this and see if any breaks result, + this takes care of some strange cases */ + if (Cache[I].CandidateVer != Cache[I].InstallVer && + I->CurrentVer != 0 && Cache[I].InstallVer != 0 && + (Flags[I->ID] & PreInstalled) != 0 && + (Flags[I->ID] & Protected) == 0) + { + if (Debug == true) + cout << " Try to Re-Instate " << I.Name() << endl; + int OldBreaks = Cache.BrokenCount(); + pkgCache::Version *OldVer = Cache[I].InstallVer; + + Cache.MarkInstall(I,false); + if (Cache[I].InstBroken() == true || + OldBreaks < Cache.BrokenCount()) + { + if (OldVer == 0) + Cache.MarkDelete(I); + else + Cache.MarkKeep(I); + } + else + if (Debug == true) + cout << "Re-Instated " << I.Name() << endl; + } + + if (Cache[I].InstallVer == 0 || Cache[I].InstBroken() == false) + continue; + + // Isolate the problem dependency + PackageKill KillList[100]; + PackageKill *LEnd = KillList; + for (pkgCache::DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); D.end() == false;) + { + // Compute a single dependency element (glob or) + pkgCache::DepIterator Start = D; + pkgCache::DepIterator End = D; + unsigned char State = 0; + for (bool LastOR = true; D.end() == false && LastOR == true; D++) + { + State |= Cache[D]; + LastOR = (D->CompareOp & pkgOP_OR) == pkgOP_OR; + if (LastOR == true) + End = D; + } + + // We only worry about critical deps. + if (End.IsCritical() != true) + continue; + + // Dep is ok + if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) + continue; + + // Hm, the group is broken.. I have no idea how to handle this + if (Start != End) + { + cout << "Note, a broken or group was found in " << I.Name() << "." << endl; + Cache.MarkDelete(I); + break; + } + + if (Debug == true) + cout << "Package " << I.Name() << " has broken dep on " << End.TargetPkg().Name() << endl; + + /* Conflicts is simple, decide if we should remove this package + or the conflicted one */ + pkgCache::Version **VList = End.AllTargets(); + bool Done = false; + for (pkgCache::Version **V = VList; *V != 0; V++) + { + pkgCache::VerIterator Ver(Cache,*V); + pkgCache::PkgIterator Pkg = Ver.ParentPkg(); + + if (Debug == true) + cout << " Considering " << Pkg.Name() << ' ' << (int)Scores[Pkg->ID] << + " as a solution to " << I.Name() << ' ' << (int)Scores[I->ID] << endl; + if (Scores[I->ID] <= Scores[Pkg->ID] || + ((Cache[End] & pkgDepCache::DepGNow) == 0 && + End->Type != pkgDEP_Conflicts)) + { + if ((Flags[I->ID] & Protected) != 0) + continue; + + // See if a keep will do + Cache.MarkKeep(I); + if (Cache[I].InstBroken() == false) + { + if (Debug == true) + cout << " Holding Back " << I.Name() << " rather than change " << End.TargetPkg().Name() << endl; + } + else + { + if (BrokenFix == false || DoUpgrade(I) == false) + { + if (Debug == true) + cout << " Removing " << I.Name() << " rather than change " << End.TargetPkg().Name() << endl; + Cache.MarkDelete(I); + if (Counter > 1) + Scores[I->ID] = Scores[Pkg->ID]; + } + } + + Change = true; + Done = true; + break; + } + else + { + // Skip this if it is protected + if ((Flags[Pkg->ID] & Protected) != 0) + continue; + + LEnd->Pkg = Pkg; + LEnd->Dep = End; + LEnd++; + if (End->Type != pkgDEP_Conflicts) + break; + } + } + + // Hm, nothing can possibly satisify this dep. Nuke it. + if (VList[0] == 0 && End->Type != pkgDEP_Conflicts) + { + Cache.MarkKeep(I); + if (Cache[I].InstBroken() == false) + { + if (Debug == true) + cout << " Holding Back " << I.Name() << " because I can't find " << End.TargetPkg().Name() << endl; + } + else + { + if (Debug == true) + cout << " Removing " << I.Name() << " because I can't find " << End.TargetPkg().Name() << endl; + Cache.MarkDelete(I); + } + + Change = true; + Done = true; + } + + delete [] VList; + if (Done == true) + break; + } + + // Apply the kill list now + if (Cache[I].InstallVer != 0) + for (PackageKill *J = KillList; J != LEnd; J++) + { + Change = true; + if ((Cache[J->Dep] & pkgDepCache::DepGNow) == 0) + { + if (J->Dep->Type == pkgDEP_Conflicts) + { + if (Debug == true) + cout << " Fixing " << I.Name() << " via remove of " << J->Pkg.Name() << endl; + Cache.MarkDelete(J->Pkg); + } + } + else + { + if (Debug == true) + cout << " Fixing " << I.Name() << " via keep of " << J->Pkg.Name() << endl; + Cache.MarkKeep(J->Pkg); + } + + if (Counter > 1) + Scores[J->Pkg->ID] = Scores[I->ID]; + } + } + } + + if (Debug == true) + cout << "Done" << endl; + + delete [] Scores; + delete [] PList; + + if (Cache.BrokenCount() != 0) + return _error->Error("Internal error, ScoredFix generated breaks."); + + return true; +} + /*}}}*/ diff --git a/apt-pkg/algorithms.h b/apt-pkg/algorithms.h new file mode 100644 index 000000000..0dd77d940 --- /dev/null +++ b/apt-pkg/algorithms.h @@ -0,0 +1,91 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: algorithms.h,v 1.1 1998/07/07 04:17:00 jgg Exp $ +/* ###################################################################### + + Algorithms - A set of misc algorithms + + This simulate class displays what the ordering code has done and + analyses it with a fresh new dependency cache. In this way we can + see all of the effects of an upgrade run. + + pkgDistUpgrade computes an upgrade that causes as many packages as + possible to move to the newest verison. + + pkgApplyStatus sets the target state based on the content of the status + field in the status file. It is important to get proper crash recovery. + + ##################################################################### */ + /*}}}*/ +// Header section: pkglib +#ifndef PKGLIB_ALGORITHMS_H +#define PKGLIB_ALGORITHMS_H + +#ifdef __GNUG__ +#pragma interface "pkglib/algorithms.h" +#endif + +#include +#include + +class pkgSimulate : public pkgPackageManager +{ + protected: + + unsigned char *Flags; + + pkgDepCache Sim; + + // The Actuall installation implementation + virtual bool Install(PkgIterator Pkg,string File); + virtual bool Configure(PkgIterator Pkg); + virtual bool Remove(PkgIterator Pkg); + void ShortBreaks(); + + public: + + pkgSimulate(pkgDepCache &Cache); +}; + +class pkgProblemResolver +{ + pkgDepCache &Cache; + typedef pkgCache::PkgIterator PkgIterator; + typedef pkgCache::VerIterator VerIterator; + typedef pkgCache::DepIterator DepIterator; + typedef pkgCache::PrvIterator PrvIterator; + typedef pkgCache::Version Version; + typedef pkgCache::Package Package; + + enum Flags {Protected = (1 << 0), PreInstalled = (1 << 1), + Upgradable = (1 << 2)}; + signed short *Scores; + unsigned char *Flags; + bool Debug; + + // Sort stuff + static pkgProblemResolver *This; + static int ScoreSort(const void *a,const void *b); + + struct PackageKill + { + PkgIterator Pkg; + DepIterator Dep; + }; + + void MakeScores(); + bool DoUpgrade(pkgCache::PkgIterator Pkg); + + public: + + inline void Protect(pkgCache::PkgIterator Pkg) {Flags[Pkg->ID] |= Protected;}; + bool Resolve(bool BrokenFix = false); + + pkgProblemResolver(pkgDepCache &Cache); +}; + +bool pkgDistUpgrade(pkgDepCache &Cache); +bool pkgApplyStatus(pkgDepCache &Cache); +bool pkgFixBroken(pkgDepCache &Cache); + +#endif diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h index f12491fe1..f234526db 100644 --- a/apt-pkg/cacheiterators.h +++ b/apt-pkg/cacheiterators.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: cacheiterators.h,v 1.3 1998/07/05 05:33:52 jgg Exp $ +// $Id: cacheiterators.h,v 1.4 1998/07/07 04:17:00 jgg Exp $ /* ###################################################################### Cache Iterators - Iterators for navigating the cache structure @@ -21,7 +21,8 @@ The DepIterator can iterate over two lists, a list of 'version depends' or a list of 'package reverse depends'. The type is determined by the structure passed to the constructor, which should be the structure - that has the depends pointer as a member. + that has the depends pointer as a member. The provide iterator has the + same system. This header is not user includable, please use pkglib/pkgcache.h @@ -31,6 +32,10 @@ #ifndef PKGLIB_CACHEITERATORS_H #define PKGLIB_CACHEITERATORS_H +#ifdef __GNUG__ +#pragma interface "pkglib/cacheiterators.h" +#endif + // Package Iterator class pkgCache::PkgIterator { @@ -57,6 +62,7 @@ class pkgCache::PkgIterator inline Package const &operator *() const {return *Pkg;}; inline operator Package *() {return Pkg == Owner->PkgP?0:Pkg;}; inline operator Package const *() const {return Pkg == Owner->PkgP?0:Pkg;}; + inline const char *Name() const {return Pkg->Name == 0?0:Owner->StrP + Pkg->Name;}; inline const char *Section() const {return Pkg->Section == 0?0:Owner->StrP + Pkg->Section;}; inline const char *TargetDist() const {return Pkg->TargetDist == 0?0:Owner->StrP + Pkg->TargetDist;}; @@ -111,13 +117,14 @@ class pkgCache::VerIterator inline Version const &operator *() const {return *Ver;}; inline operator Version *() {return Ver == Owner.VerP?0:Ver;}; inline operator Version const *() const {return Ver == Owner.VerP?0:Ver;}; + inline const char *VerStr() const {return Ver->VerStr == 0?0:Owner.StrP + Ver->VerStr;}; inline const char *Section() const {return Ver->Section == 0?0:Owner.StrP + Ver->Section;}; inline PkgIterator ParentPkg() const {return PkgIterator(Owner,Owner.PkgP + Ver->ParentPkg);}; inline DepIterator DependsList() const; inline PrvIterator ProvidesList() const; - inline unsigned long Index() const {return Ver - Owner.VerP;}; inline VerFileIterator FileList() const; + inline unsigned long Index() const {return Ver - Owner.VerP;}; inline VerIterator(pkgCache &Owner,Version *Trg = 0) : Ver(Trg), Owner(Owner) { @@ -154,16 +161,17 @@ class pkgCache::DepIterator inline Dependency const &operator *() const {return *Dep;}; inline operator Dependency *() {return Dep == Owner->DepP?0:Dep;}; inline operator Dependency const *() const {return Dep == Owner->DepP?0:Dep;}; + inline const char *TargetVer() const {return Dep->Version == 0?0:Owner->StrP + Dep->Version;}; inline PkgIterator TargetPkg() {return PkgIterator(*Owner,Owner->PkgP + Dep->Package);}; - Version **AllTargets(); - bool SmartTargetPkg(PkgIterator &Result); inline PkgIterator SmartTargetPkg() {PkgIterator R(*Owner);SmartTargetPkg(R);return R;}; inline VerIterator ParentVer() {return VerIterator(*Owner,Owner->VerP + Dep->ParentVer);}; inline PkgIterator ParentPkg() {return PkgIterator(*Owner,Owner->PkgP + Owner->VerP[Dep->ParentVer].ParentPkg);}; - bool IsCritical(); inline bool Reverse() {return Type == DepRev;}; inline unsigned long Index() const {return Dep - Owner->DepP;}; + bool IsCritical(); + Version **AllTargets(); + bool SmartTargetPkg(PkgIterator &Result); inline DepIterator(pkgCache &Owner,Dependency *Trg,Version * = 0) : Dep(Trg), Type(DepVer), Owner(&Owner) @@ -208,6 +216,7 @@ class pkgCache::PrvIterator inline Provides const &operator *() const {return *Prv;}; inline operator Provides *() {return Prv == Owner->ProvideP?0:Prv;}; inline operator Provides const *() const {return Prv == Owner->ProvideP?0:Prv;}; + inline const char *Name() const {return Owner->StrP + Owner->PkgP[Prv->ParentPkg].Name;}; inline const char *ProvideVersion() const {return Prv->ProvideVersion == 0?0:Owner->StrP + Prv->ProvideVersion;}; inline PkgIterator ParentPkg() {return PkgIterator(*Owner,Owner->PkgP + Prv->ParentPkg);}; diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc new file mode 100644 index 000000000..6d937d657 --- /dev/null +++ b/apt-pkg/contrib/configuration.cc @@ -0,0 +1,137 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: configuration.cc,v 1.1 1998/07/07 04:17:10 jgg Exp $ +/* ###################################################################### + + Configuration Class + + This class provides a configuration file and command line parser + for a tree-oriented configuration environment. All runtime configuration + is stored in here. + + ##################################################################### */ + /*}}}*/ +// Include files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/configuration.h" +#endif +#include +#include + +#include + /*}}}*/ +Configuration *_config; + +// Configuration::Configuration - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +Configuration::Configuration() +{ + Root = new Item; +} + /*}}}*/ +// Configuration::Lookup - Lookup a single item /*{{{*/ +// --------------------------------------------------------------------- +/* This will lookup a single item by name below another item. It is a + helper function for the main lookup function */ +Configuration::Item *Configuration::Lookup(Item *Head,const char *S, + unsigned long Len,bool Create) +{ + int Res = 1; + Item *I = Head->Child; + Item **Last = &Head->Child; + for (; I != 0; Last = &I->Next, I = I->Next) + if ((Res = stringcasecmp(I->Value.begin(),I->Value.end(),S,S + Len)) == 0) + break; + + if (Res == 0) + return I; + if (Create == false) + return 0; + + I = new Item; + I->Value = string(S,Len); + I->Next = *Last; + *Last = I; + return I; +} + /*}}}*/ +// Configuration::Lookup - Lookup a fully scoped item /*{{{*/ +// --------------------------------------------------------------------- +/* This performs a fully scoped lookup of a given name, possibly creating + new items */ +Configuration::Item *Configuration::Lookup(const char *Name,bool Create) +{ + const char *Start = Name; + const char *End = Start + strlen(Name); + const char *TagEnd = Name; + Item *Itm = Root; + for (; End - TagEnd > 2; TagEnd++) + { + if (TagEnd[0] == ':' && TagEnd[1] == ':') + { + Itm = Lookup(Itm,Start,TagEnd - Start,Create); + if (Itm == 0) + return 0; + TagEnd = Start = TagEnd + 2; + } + } + + Itm = Lookup(Itm,Start,End - Start,Create); + if (Itm == 0) + return 0; + return Itm; +} + /*}}}*/ +// Configuration::Find - Find a value /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string Configuration::Find(const char *Name,const char *Default) +{ + Item *Itm = Lookup(Name,false); + if (Itm == 0 || Itm->Value.empty() == true) + return Default; + return Itm->Value; +} + /*}}}*/ +// Configuration::FindI - Find an integer value /*{{{*/ +// --------------------------------------------------------------------- +/* */ +int Configuration::FindI(const char *Name,int Default) +{ + Item *Itm = Lookup(Name,false); + if (Itm == 0 || Itm->Value.empty() == true) + return Default; + + char *End; + int Res = strtol(Itm->Value.c_str(),&End,0); + if (End == Itm->Value.c_str()) + return Default; + + return Res; +} + /*}}}*/ +// Configuration::Set - Set a value /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void Configuration::Set(const char *Name,string Value) +{ + Item *Itm = Lookup(Name,true); + if (Itm == 0) + return; + Itm->Value = Value; +} + /*}}}*/ +// Configuration::Set - Set an integer value /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void Configuration::Set(const char *Name,int Value) +{ + Item *Itm = Lookup(Name,true); + if (Itm == 0) + return; + char S[300]; + snprintf(S,sizeof(S),"%i",Value); + Itm->Value = S; +} + /*}}}*/ diff --git a/apt-pkg/contrib/configuration.h b/apt-pkg/contrib/configuration.h new file mode 100644 index 000000000..7476346ef --- /dev/null +++ b/apt-pkg/contrib/configuration.h @@ -0,0 +1,61 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: configuration.h,v 1.1 1998/07/07 04:17:10 jgg Exp $ +/* ###################################################################### + + Configuration Class + + This class provides a configuration file and command line parser + for a tree-oriented configuration environment. All runtime configuration + is stored in here. + + Each configuration name is given as a fully scoped string such as + Foo::Bar + And has associated with it a text string. The Configuration class only + provides storage and lookup for this tree, other classes provide + configuration file formats (and parsers/emitters if needed). + + Most things can get by quite happily with, + cout << _config->Find("Foo::Bar") << endl; + + ##################################################################### */ + /*}}}*/ +// Header section: pkglib +#ifndef PKGLIB_TAGFILE_H +#define PKGLIB_TAGFILE_H + +#ifdef __GNUG__ +#pragma interface "pkglib/configuration.h" +#endif + +#include + +class Configuration +{ + struct Item + { + string Value; + string Tag; + Item *Child; + Item *Next; + Item() : Child(0), Next(0) {}; + }; + Item *Root; + + Item *Lookup(Item *Head,const char *S,unsigned long Len,bool Create); + Item *Lookup(const char *Name,bool Create); + + public: + + string Find(const char *Name,const char *Default = 0); + int FindI(const char *Name,int Default = 0); + + void Set(const char *Name,string Value); + void Set(const char *Name,int Value); + + Configuration(); +}; + +extern Configuration *_config; + +#endif diff --git a/apt-pkg/contrib/error.cc b/apt-pkg/contrib/error.cc index 59d2b8c8b..d1ea1b87b 100644 --- a/apt-pkg/contrib/error.cc +++ b/apt-pkg/contrib/error.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: error.cc,v 1.1 1998/07/02 02:58:13 jgg Exp $ +// $Id: error.cc,v 1.2 1998/07/07 04:17:10 jgg Exp $ /* ###################################################################### Global Erorr Class - Global error mechanism @@ -14,6 +14,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/error.h" +#endif + #include #include #include @@ -27,7 +31,7 @@ GlobalError *_error = new GlobalError; // GlobalError::GlobalError - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -GlobalError::GlobalError() : PendingFlag(false) +GlobalError::GlobalError() : List(0), PendingFlag(false) { } /*}}}*/ @@ -49,10 +53,10 @@ bool GlobalError::Errno(const char *Function,const char *Description,...) sprintf(S + strlen(S)," - %s (%i %s)",Function,errno,strerror(errno)); // Put it on the list - Item Itm; - Itm.Text = S; - Itm.Error = true; - List.push_back(Itm); + Item *Itm = new Item; + Itm->Text = S; + Itm->Error = true; + Insert(Itm); PendingFlag = true; @@ -72,10 +76,10 @@ bool GlobalError::Error(const char *Description,...) vsprintf(S,Description,args); // Put it on the list - Item Itm; - Itm.Text = S; - Itm.Error = true; - List.push_back(Itm); + Item *Itm = new Item; + Itm->Text = S; + Itm->Error = true; + Insert(Itm); PendingFlag = true; @@ -95,10 +99,10 @@ bool GlobalError::Warning(const char *Description,...) vsprintf(S,Description,args); // Put it on the list - Item Itm; - Itm.Text = S; - Itm.Error = false; - List.push_back(Itm); + Item *Itm = new Item; + Itm->Text = S; + Itm->Error = false; + Insert(Itm); return false; } @@ -109,12 +113,17 @@ bool GlobalError::Warning(const char *Description,...) true if the message is an error. */ bool GlobalError::PopMessage(string &Text) { - bool Ret = List.front().Error; - Text = List.front().Text; - List.erase(List.begin()); - + if (List == 0) + return false; + + bool Ret = List->Error; + Text = List->Text; + Item *Old = List; + List = List->Next; + delete Old; + // This really should check the list to see if only warnings are left.. - if (empty()) + if (List == 0) PendingFlag = false; return Ret; @@ -137,3 +146,30 @@ void GlobalError::DumpErrors() } } /*}}}*/ +// GlobalError::Discard - Discard /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void GlobalError::Discard() +{ + while (List != 0) + { + Item *Old = List; + List = List->Next; + delete Old; + } + + PendingFlag = false; +}; + /*}}}*/ +// GlobalError::Insert - Insert a new item at the end /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void GlobalError::Insert(Item *Itm) +{ + Item **End = &List; + for (Item *I = List; I != 0; I = I->Next) + End = &I->Next; + Itm->Next = *End; + *End = Itm; +} + /*}}}*/ diff --git a/apt-pkg/contrib/error.h b/apt-pkg/contrib/error.h index 06b998e5e..06367592b 100644 --- a/apt-pkg/contrib/error.h +++ b/apt-pkg/contrib/error.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: error.h,v 1.1 1998/07/02 02:58:13 jgg Exp $ +// $Id: error.h,v 1.2 1998/07/07 04:17:11 jgg Exp $ /* ###################################################################### Global Erorr Class - Global error mechanism @@ -41,8 +41,11 @@ #ifndef PKGLIB_ERROR_H #define PKGLIB_ERROR_H +#ifdef __GNUG__ +#pragma interface "pkglib/error.h" +#endif + #include -#include class GlobalError { @@ -50,10 +53,12 @@ class GlobalError { string Text; bool Error; + Item *Next; }; - vector List; + Item *List; bool PendingFlag; + void Insert(Item *I); public: @@ -67,9 +72,9 @@ class GlobalError // Simple accessors inline bool PendingError() {return PendingFlag;}; - inline bool empty() {return List.empty();}; + inline bool empty() {return List == 0;}; bool PopMessage(string &Text); - void Discard() {List.erase(List.begin(),List.end()); PendingFlag = false;}; + void Discard(); // Usefull routine to dump to cerr void DumpErrors(); diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index 6c6441ef6..29b12bef1 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: fileutl.cc,v 1.2 1998/07/04 05:57:41 jgg Exp $ +// $Id: fileutl.cc,v 1.3 1998/07/07 04:17:12 jgg Exp $ /* ###################################################################### File Utilities @@ -14,6 +14,9 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/fileutl.h" +#endif #include #include diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h index 1b6666843..aa2d2ee2e 100644 --- a/apt-pkg/contrib/fileutl.h +++ b/apt-pkg/contrib/fileutl.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: fileutl.h,v 1.1 1998/07/02 02:58:13 jgg Exp $ +// $Id: fileutl.h,v 1.2 1998/07/07 04:17:13 jgg Exp $ /* ###################################################################### File Utilities @@ -22,6 +22,10 @@ #ifndef PKGLIB_FILEUTL_H #define PKGLIB_FILEUTL_H +#ifdef __GNUG__ +#pragma interface "pkglib/fileutl.h" +#endif + #include class File diff --git a/apt-pkg/contrib/mmap.cc b/apt-pkg/contrib/mmap.cc index 41ea02aec..c0cc13f00 100644 --- a/apt-pkg/contrib/mmap.cc +++ b/apt-pkg/contrib/mmap.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: mmap.cc,v 1.3 1998/07/04 22:32:15 jgg Exp $ +// $Id: mmap.cc,v 1.4 1998/07/07 04:17:14 jgg Exp $ /* ###################################################################### MMap Class - Provides 'real' mmap or a faked mmap using read(). @@ -21,6 +21,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/mmap.h" +#endif + #define _BSD_SOURCE #include #include diff --git a/apt-pkg/contrib/mmap.h b/apt-pkg/contrib/mmap.h index c7ddf685d..55feda741 100644 --- a/apt-pkg/contrib/mmap.h +++ b/apt-pkg/contrib/mmap.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: mmap.h,v 1.2 1998/07/04 05:57:43 jgg Exp $ +// $Id: mmap.h,v 1.3 1998/07/07 04:17:15 jgg Exp $ /* ###################################################################### MMap Class - Provides 'real' mmap or a faked mmap using read(). @@ -26,6 +26,10 @@ #ifndef PKGLIB_MMAP_H #define PKGLIB_MMAP_H +#ifdef __GNUG__ +#pragma interface "pkglib/mmap.h" +#endif + #include #include diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc new file mode 100644 index 000000000..14965f91e --- /dev/null +++ b/apt-pkg/contrib/strutl.cc @@ -0,0 +1,343 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: strutl.cc,v 1.1 1998/07/07 04:17:16 jgg Exp $ +/* ###################################################################### + + String Util - Some usefull string functions. + + strstrip - Remove whitespace from the front and end of a line. + + This source is placed in the Public Domain, do with it what you will + It was originally written by Jason Gunthorpe + + ##################################################################### */ + /*}}}*/ +// Includes /*{{{*/ +#include +#include +#include +#include + /*}}}*/ + +// strstrip - Remove white space from the front and back of a string /*{{{*/ +// --------------------------------------------------------------------- +/* This is handy to use when parsing a file. It also removes \n's left + over from fgets and company */ +char *_strstrip(char *String) +{ + for (;*String != 0 && (*String == ' ' || *String == '\t'); String++); + + if (*String == 0) + return String; + + char *End = String + strlen(String) - 1; + for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' || + *End == '\r'); End--); + End++; + *End = 0; + return String; +}; + /*}}}*/ +// strtabexpand - Converts tabs into 8 spaces /*{{{*/ +// --------------------------------------------------------------------- +/* */ +char *_strtabexpand(char *String,size_t Len) +{ + for (char *I = String; I != I + Len && *I != 0; I++) + { + if (*I != '\t') + continue; + if (I + 8 > String + Len) + { + *I = 0; + return String; + } + + /* Assume the start of the string is 0 and find the next 8 char + division */ + int Len; + if (String == I) + Len = 1; + else + Len = 8 - ((String - I) % 8); + Len -= 2; + if (Len <= 0) + { + *I = ' '; + continue; + } + + memmove(I + Len,I + 1,strlen(I) + 1); + for (char *J = I; J + Len != I; *I = ' ', I++); + } + return String; +} + /*}}}*/ +// ParseQuoteWord - Parse a single word out of a string /*{{{*/ +// --------------------------------------------------------------------- +/* This grabs a single word, converts any % escaped characters to their + proper values and advances the pointer. Double quotes are understood + and striped out as well. */ +bool ParseQuoteWord(const char *&String,string &Res) +{ + // Skip leading whitespace + const char *C = String; + for (;*C != 0 && *C == ' '; C++); + if (*C == 0) + return false; + + // Jump to the next word + for (;*C != 0 && *C != ' '; C++) + { + if (*C == '"') + { + for (C++;*C != 0 && *C != '"'; C++); + if (*C == 0) + return false; + } + } + + // Now de-quote characters + char Buffer[1024]; + char Tmp[3]; + const char *Start = String; + char *I; + for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++) + { + if (*Start == '%' && Start + 2 < C) + { + Tmp[0] = Start[1]; + Tmp[1] = Start[2]; + Tmp[3] = 0; + *I = (char)strtol(Tmp,0,16); + Start += 3; + continue; + } + if (*Start != '"') + *I = *Start; + else + I--; + Start++; + } + *I = 0; + Res = Buffer; + + // Skip ending white space + for (;*C != 0 && *C == ' '; C++); + String = C; + return true; +} + /*}}}*/ +// QuoteString - Convert a string into quoted from /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string QuoteString(string Str,const char *Bad) +{ + string Res; + for (string::iterator I = Str.begin(); I != Str.end(); I++) + { + if (strchr(Bad,*I) != 0 || isprint(*I) == 0 || + *I <= 0x20 || *I >= 0x7F) + { + char Buf[10]; + sprintf(Buf,"%%%02x",(int)*I); + Res += Buf; + } + else + Res += *I; + } + return Res; +} + /*}}}*/ +// SizeToStr - Convert a long into a human readable size /*{{{*/ +// --------------------------------------------------------------------- +/* A max of 4 digits are shown before conversion to the next highest unit. The + max length of the string will be 5 chars unless the size is > 10 + YottaBytes (E24) */ +string SizeToStr(double Size) +{ + char S[300]; + double ASize; + if (Size >= 0) + ASize = Size; + else + ASize = -1*Size; + + /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes, + ExaBytes, ZettaBytes, YottaBytes */ + char Ext[] = {'b','k','M','G','T','P','E','Z','Y'}; + int I = 0; + while (I <= 8) + { + if (ASize < 100 && I != 0) + { + sprintf(S,"%.1f%c",ASize,Ext[I]); + break; + } + + if (ASize < 10000) + { + sprintf(S,"%.0f%c",ASize,Ext[I]); + break; + } + ASize /= 1000.0; + I++; + } + + return S; +} + /*}}}*/ +// TimeToStr - Convert the time into a string /*{{{*/ +// --------------------------------------------------------------------- +/* Converts a number of seconds to a hms format */ +string TimeToStr(unsigned long Sec) +{ + char S[300]; + + while (1) + { + if (Sec > 60*60*24) + { + sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60); + break; + } + + if (Sec > 60*60) + { + sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 60); + break; + } + + if (Sec > 60) + { + sprintf(S,"%lim%lis",Sec/60,Sec % 60); + break; + } + + sprintf(S,"%lis",Sec); + break; + } + + return S; +} + /*}}}*/ +// SubstVar - Substitute a string for another string /*{{{*/ +// --------------------------------------------------------------------- +/* This replaces all occurances of Subst with Contents in Str. */ +string SubstVar(string Str,string Subst,string Contents) +{ + string::size_type Pos; + string::size_type OldPos = 0; + string Temp; + + while (OldPos < Str.length() && + (Pos = Str.find(Subst,OldPos)) != string::npos) + { + Temp += string(Str,OldPos,Pos) + Contents; + OldPos = Pos + Subst.length(); + } + + if (OldPos == 0) + return Str; + + return Temp + string(Str,OldPos); +} + /*}}}*/ +// Base64Encode - Base64 Encoding routine for short strings /*{{{*/ +// --------------------------------------------------------------------- +/* This routine performs a base64 transformation on a string. It was ripped + from wget and then patched and bug fixed. + + This spec can be found in rfc2045 */ +string Base64Encode(string S) +{ + // Conversion table. + static char tbl[64] = {'A','B','C','D','E','F','G','H', + 'I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X', + 'Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3', + '4','5','6','7','8','9','+','/'}; + + // Pre-allocate some space + string Final; + Final.reserve((4*S.length() + 2)/3 + 2); + + /* Transform the 3x8 bits to 4x6 bits, as required by + base64. */ + for (string::const_iterator I = S.begin(); I < S.end(); I += 3) + { + char Bits[3] = {0,0,0}; + Bits[0] = I[0]; + if (I + 1 < S.end()) + Bits[1] = I[1]; + if (I + 2 < S.end()) + Bits[2] = I[2]; + + Final += tbl[Bits[0] >> 2]; + Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)]; + + if (I + 1 >= S.end()) + break; + + Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)]; + + if (I + 2 >= S.end()) + break; + + Final += tbl[Bits[2] & 0x3f]; + } + + /* Apply the padding elements, this tells how many bytes the remote + end should discard */ + if (S.length() % 3 == 2) + Final += '='; + if (S.length() % 3 == 1) + Final += "=="; + + return Final; +} + /*}}}*/ +// stringcmp - Arbitary string compare /*{{{*/ +// --------------------------------------------------------------------- +/* This safely compares two non-null terminated strings of arbitary + length */ +int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd) +{ + for (; A != AEnd && B != BEnd; A++, B++) + if (*A != *B) + break; + + if (A == AEnd && B == BEnd) + return 0; + if (A == AEnd) + return 1; + if (B == BEnd) + return -1; + if (*A < *B) + return -1; + return 1; +} + /*}}}*/ +// stringcasecmp - Arbitary case insensitive string compare /*{{{*/ +// --------------------------------------------------------------------- +/* */ +int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd) +{ + for (; A != AEnd && B != BEnd; A++, B++) + if (toupper(*A) != toupper(*B)) + break; + + if (A == AEnd && B == BEnd) + return 0; + if (A == AEnd) + return 1; + if (B == BEnd) + return -1; + if (toupper(*A) < toupper(*B)) + return -1; + return 1; +} + /*}}}*/ diff --git a/apt-pkg/contrib/strutl.h b/apt-pkg/contrib/strutl.h new file mode 100644 index 000000000..44a10c2f3 --- /dev/null +++ b/apt-pkg/contrib/strutl.h @@ -0,0 +1,36 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: strutl.h,v 1.1 1998/07/07 04:17:16 jgg Exp $ +/* ###################################################################### + + String Util - These are some usefull string functions + + _strstrip is a function to remove whitespace from the front and end + of a string. + + This source is placed in the Public Domain, do with it what you will + It was originally written by Jason Gunthorpe + + ##################################################################### */ + /*}}}*/ +// This is a private header +// Header section: / +#ifndef STRUTL_H +#define STRUTL_H + +#include +#include + +char *_strstrip(char *String); +char *_strtabexpand(char *String,size_t Len); +bool ParseQuoteWord(const char *&String,string &Res); +string QuoteString(string Str,const char *Bad); +string SizeToStr(double Bytes); +string TimeToStr(unsigned long Sec); +string SubstVar(string Str,string Subst,string Contents); +string Base64Encode(string Str); + +int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd); +int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd); + +#endif diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc index 4c37eb775..5fbb43a9f 100644 --- a/apt-pkg/deb/deblistparser.cc +++ b/apt-pkg/deb/deblistparser.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: deblistparser.cc,v 1.3 1998/07/05 05:34:00 jgg Exp $ +// $Id: deblistparser.cc,v 1.4 1998/07/07 04:17:16 jgg Exp $ /* ###################################################################### Package Cache Generator - Generator for the cache structure. @@ -147,27 +147,27 @@ bool debListParser::NewVersion(pkgCache::VerIterator Ver) const char *Stop; if (Section.Find("Priority",Start,Stop) == true) { - WordList PrioList[] = {{"important",pkgCache::Important}, - {"required",pkgCache::Required}, - {"standard",pkgCache::Standard}, - {"optional",pkgCache::Optional}, - {"extra",pkgCache::Extra}}; + WordList PrioList[] = {{"important",pkgCache::State::Important}, + {"required",pkgCache::State::Required}, + {"standard",pkgCache::State::Standard}, + {"optional",pkgCache::State::Optional}, + {"extra",pkgCache::State::Extra}}; if (GrabWord(string(Start,Stop-Start),PrioList, _count(PrioList),Ver->Priority) == false) return _error->Error("Malformed Priority line"); } - if (ParseDepends(Ver,"Depends",pkgCache::Depends) == false) + if (ParseDepends(Ver,"Depends",pkgCache::Dep::Depends) == false) return false; - if (ParseDepends(Ver,"PreDepends",pkgCache::PreDepends) == false) + if (ParseDepends(Ver,"PreDepends",pkgCache::Dep::PreDepends) == false) return false; - if (ParseDepends(Ver,"Suggests",pkgCache::Suggests) == false) + if (ParseDepends(Ver,"Suggests",pkgCache::Dep::Suggests) == false) return false; - if (ParseDepends(Ver,"Recommends",pkgCache::Recommends) == false) + if (ParseDepends(Ver,"Recommends",pkgCache::Dep::Recommends) == false) return false; - if (ParseDepends(Ver,"Conflicts",pkgCache::Conflicts) == false) + if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false) return false; - if (ParseDepends(Ver,"Replaces",pkgCache::Depends) == false) + if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Depends) == false) return false; if (ParseProvides(Ver) == false) @@ -186,9 +186,9 @@ bool debListParser::UsePackage(pkgCache::PkgIterator Pkg, if (Pkg->Section == 0) if ((Pkg->Section = UniqFindTagWrite("Section")) == 0) return false; - if (HandleFlag("Essential",Pkg->Flags,pkgCache::Essential) == false) + if (HandleFlag("Essential",Pkg->Flags,pkgCache::Flag::Essential) == false) return false; - if (HandleFlag("Immediate-Configure",Pkg->Flags,pkgCache::ImmediateConf) == false) + if (HandleFlag("Immediate-Configure",Pkg->Flags,pkgCache::Flag::ImmediateConf) == false) return false; if (ParseStatus(Pkg,Ver) == false) return false; @@ -223,11 +223,11 @@ bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg, return _error->Error("Malformed Status line"); // Process the want field - WordList WantList[] = {{"unknown",pkgCache::Unknown}, - {"install",pkgCache::Install}, - {"hold",pkgCache::Hold}, - {"deinstall",pkgCache::DeInstall}, - {"purge",pkgCache::Purge}}; + WordList WantList[] = {{"unknown",pkgCache::State::Unknown}, + {"install",pkgCache::State::Install}, + {"hold",pkgCache::State::Hold}, + {"deinstall",pkgCache::State::DeInstall}, + {"purge",pkgCache::State::Purge}}; if (GrabWord(string(Start,I-Start),WantList, _count(WantList),Pkg->SelectedState) == false) return _error->Error("Malformed 1st word in the Status line"); @@ -240,10 +240,10 @@ bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg, return _error->Error("Malformed status line, no 2nd word"); // Process the flag field - WordList FlagList[] = {{"ok",pkgCache::Ok}, - {"reinstreq",pkgCache::ReInstReq}, - {"hold",pkgCache::HoldInst}, - {"hold-reinstreq",pkgCache::HoldReInstReq}}; + WordList FlagList[] = {{"ok",pkgCache::State::Ok}, + {"reinstreq",pkgCache::State::ReInstReq}, + {"hold",pkgCache::State::HoldInst}, + {"hold-reinstreq",pkgCache::State::HoldReInstReq}}; if (GrabWord(string(Start,I-Start),FlagList, _count(FlagList),Pkg->InstState) == false) return _error->Error("Malformed 2nd word in the Status line"); @@ -256,15 +256,15 @@ bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg, return _error->Error("Malformed Status line, no 3rd word"); // Process the flag field - WordList StatusList[] = {{"not-installed",pkgCache::NotInstalled}, - {"unpacked",pkgCache::UnPacked}, - {"half-configured",pkgCache::HalfConfigured}, - {"installed",pkgCache::Installed}, - {"uninstalled",pkgCache::UnInstalled}, - {"half-installed",pkgCache::HalfInstalled}, - {"config-files",pkgCache::ConfigFiles}, - {"post-inst-failed",pkgCache::HalfConfigured}, - {"removal-failed",pkgCache::HalfInstalled}}; + WordList StatusList[] = {{"not-installed",pkgCache::State::NotInstalled}, + {"unpacked",pkgCache::State::UnPacked}, + {"half-configured",pkgCache::State::HalfConfigured}, + {"installed",pkgCache::State::Installed}, + {"uninstalled",pkgCache::State::UnInstalled}, + {"half-installed",pkgCache::State::HalfInstalled}, + {"config-files",pkgCache::State::ConfigFiles}, + {"post-inst-failed",pkgCache::State::HalfConfigured}, + {"removal-failed",pkgCache::State::HalfInstalled}}; if (GrabWord(string(Start,I-Start),StatusList, _count(StatusList),Pkg->CurrentState) == false) return _error->Error("Malformed 3rd word in the Status line"); @@ -273,8 +273,8 @@ bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg, version as well. Only if it is actually installed.. Otherwise the interesting dpkg handling of the status file creates bogus entries. */ - if (!(Pkg->CurrentState == pkgCache::NotInstalled || - Pkg->CurrentState == pkgCache::ConfigFiles)) + if (!(Pkg->CurrentState == pkgCache::State::NotInstalled || + Pkg->CurrentState == pkgCache::State::ConfigFiles)) { if (Ver.end() == true) _error->Warning("Encountered status field in a non-version description"); @@ -330,19 +330,19 @@ const char *debListParser::ParseDepends(const char *Start,const char *Stop, if (*I == '=') { I++; - Op = pkgCache::LessEq; + Op = pkgCache::Dep::LessEq; break; } if (*I == '<') { I++; - Op = pkgCache::Less; + Op = pkgCache::Dep::Less; break; } // < is the same as <= and << is really Cs < for some reason - Op = pkgCache::LessEq; + Op = pkgCache::Dep::LessEq; break; case '>': @@ -350,29 +350,29 @@ const char *debListParser::ParseDepends(const char *Start,const char *Stop, if (*I == '=') { I++; - Op = pkgCache::GreaterEq; + Op = pkgCache::Dep::GreaterEq; break; } if (*I == '>') { I++; - Op = pkgCache::Greater; + Op = pkgCache::Dep::Greater; break; } // > is the same as >= and >> is really Cs > for some reason - Op = pkgCache::GreaterEq; + Op = pkgCache::Dep::GreaterEq; break; case '=': - Op = pkgCache::Equals; + Op = pkgCache::Dep::Equals; I++; break; // HACK around bad package definitions default: - Op = pkgCache::Equals; + Op = pkgCache::Dep::Equals; break; } @@ -389,13 +389,13 @@ const char *debListParser::ParseDepends(const char *Start,const char *Stop, else { Ver = string(); - Op = pkgCache::NoOp; + Op = pkgCache::Dep::NoOp; } // Skip whitespace for (;I != Stop && isspace(*I) != 0; I++); if (I != Stop && *I == '|') - Op |= pkgCache::Or; + Op |= pkgCache::Dep::Or; if (I == Stop || *I == ',' || *I == '|') { @@ -453,7 +453,7 @@ bool debListParser::ParseProvides(pkgCache::VerIterator Ver) Start = ParseDepends(Start,Stop,Package,Version,Op); if (Start == 0) return _error->Error("Problem parsing Provides line"); - if (Op != pkgCache::NoOp) + if (Op != pkgCache::Dep::NoOp) return _error->Error("Malformed provides line"); if (NewProvides(Ver,Package,Version) == false) diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc new file mode 100644 index 000000000..a7784c0a5 --- /dev/null +++ b/apt-pkg/depcache.cc @@ -0,0 +1,903 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: depcache.cc,v 1.1 1998/07/07 04:17:01 jgg Exp $ +/* ###################################################################### + + Dependency Cache - Caches Dependency information. + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/depcache.h" +#endif +#include + +#include +#include + /*}}}*/ + +// DepCache::pkgDepCache - Constructors /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgDepCache::pkgDepCache(MMap &Map) : + pkgCache(Map), PkgState(0), DepState(0) +{ + if (_error->PendingError() == false) + Init(); +} + /*}}}*/ +// DepCache::~pkgDepCache - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgDepCache::~pkgDepCache() +{ + delete [] PkgState; + delete [] DepState; +} + /*}}}*/ +// DepCache::ReMap - Regenerate the extra data for the new cache /*{{{*/ +// --------------------------------------------------------------------- +/* pkgCache's constructors call this function, but because the object is not + fully constructed at that point it will not result in this function being + called but pkgCache::ReMap will be instead.*/ +bool pkgDepCache::ReMap() +{ + if (pkgCache::ReMap() == false) + return false; + + return Init(); +} + /*}}}*/ +// DepCache::Init - Generate the initial extra structures. /*{{{*/ +// --------------------------------------------------------------------- +/* This allocats the extension buffers and initializes them. */ +bool pkgDepCache::Init() +{ + delete [] PkgState; + delete [] DepState; + PkgState = new StateCache[Head().PackageCount]; + DepState = new unsigned char[Head().DependsCount]; + memset(PkgState,0,sizeof(*PkgState)*Head().PackageCount); + memset(DepState,0,sizeof(*DepState)*Head().DependsCount); + + /* Set the current state of everything. In this state all of the + packages are kept exactly as is. See AllUpgrade */ + for (PkgIterator I = PkgBegin(); I.end() != true; I++) + { + // Find the proper cache slot + StateCache &State = PkgState[I->ID]; + State.iFlags = 0; + + // Figure out the install version + State.CandidateVer = GetCandidateVer(I); + State.InstallVer = I.CurrentVer(); + State.Mode = ModeKeep; + + State.Update(I,*this); + } + + Update(); + + return true; +} + /*}}}*/ +// DepCache::GetCandidateVer - Returns the Candidate install version /*{{{*/ +// --------------------------------------------------------------------- +/* The default just returns the target version if it exists or the + highest version. */ +pkgDepCache::VerIterator pkgDepCache::GetCandidateVer(PkgIterator Pkg) +{ + // Try to use an explicit target + if (Pkg->TargetVer == 0) + { + /* Not source versions cannot be a candidate version unless they + are already installed */ + for (VerIterator I = Pkg.VersionList(); I.end() == false; I++) + { + if (Pkg.CurrentVer() == I) + return I; + for (VerFileIterator J = I.FileList(); J.end() == false; J++) + if ((J.File()->Flags & Flag::NotSource) == 0) + return I; + } + + return VerIterator(*this,0); + } + else + return Pkg.TargetVer(); +} + /*}}}*/ +// DepCache::IsImportantDep - True if the dependency is important /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgDepCache::IsImportantDep(DepIterator Dep) +{ + return Dep.IsCritical(); +} + /*}}}*/ + +// DepCache::CheckDep - Checks a single dependency /*{{{*/ +// --------------------------------------------------------------------- +/* This first checks the dependency against the main target package and + then walks along the package provides list and checks if each provides + will be installed then checks the provides against the dep. Res will be + set to the package which was used to satisfy the dep. */ +bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) +{ + Res = Dep.TargetPkg(); + + /* Check simple depends. A depends -should- never self match but + we allow it anyhow because dpkg does. Technically it is a packaging + bug. Conflicts may never self match */ + if (Dep.TargetPkg() != Dep.ParentPkg() || Dep->Type != Dep::Conflicts) + { + PkgIterator Pkg = Dep.TargetPkg(); + // Check the base package + if (Type == NowVersion && Pkg->CurrentVer != 0) + if (pkgCheckDep(Dep.TargetVer(), + Pkg.CurrentVer().VerStr(),Dep->CompareOp) == true) + return true; + + if (Type == InstallVersion && PkgState[Pkg->ID].InstallVer != 0) + if (pkgCheckDep(Dep.TargetVer(), + PkgState[Pkg->ID].InstVerIter(*this).VerStr(), + Dep->CompareOp) == true) + return true; + + if (Type == CandidateVersion && PkgState[Pkg->ID].CandidateVer != 0) + if (pkgCheckDep(Dep.TargetVer(), + PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(), + Dep->CompareOp) == true) + return true; + } + + // Check the providing packages + PrvIterator P = Dep.TargetPkg().ProvidesList(); + PkgIterator Pkg = Dep.ParentPkg(); + for (; P.end() != true; P++) + { + /* Provides may never be applied against the same package if it is + a conflicts. See the comment above. */ + if (P.OwnerPkg() == Pkg && Dep->Type == Dep::Conflicts) + continue; + + // Check if the provides is a hit + if (Type == NowVersion) + { + if (P.OwnerPkg().CurrentVer() != P.OwnerVer()) + continue; + } + + if (Type == InstallVersion) + { + StateCache &State = PkgState[P.OwnerPkg()->ID]; + if (State.InstallVer != (Version *)P.OwnerVer()) + continue; + } + + if (Type == CandidateVersion) + { + StateCache &State = PkgState[P.OwnerPkg()->ID]; + if (State.CandidateVer != (Version *)P.OwnerVer()) + continue; + } + + // Compare the versions. + if (pkgCheckDep(Dep.TargetVer(),P.ProvideVersion(),Dep->CompareOp) == true) + { + Res = P.OwnerPkg(); + return true; + } + } + + return false; +} + /*}}}*/ +// DepCache::AddSizes - Add the packages sizes to the counters /*{{{*/ +// --------------------------------------------------------------------- +/* Call with Mult = -1 to preform the inverse opration */ +void pkgDepCache::AddSizes(const PkgIterator &Pkg,long Mult) +{ + StateCache &P = PkgState[Pkg->ID]; + + // Compute the size data + if (P.NewInstall() == true) + { + iUsrSize += Mult*P.InstVerIter(*this)->InstalledSize; + iDownloadSize += Mult*P.InstVerIter(*this)->Size; + } + + // Upgrading + if (Pkg->CurrentVer != 0 && P.InstallVer != (Version *)Pkg.CurrentVer() && + P.InstallVer != 0) + { + iUsrSize += Mult*(P.InstVerIter(*this)->InstalledSize - + Pkg.CurrentVer()->InstalledSize); + iDownloadSize += Mult*P.InstVerIter(*this)->Size; + } + + // Removing + if (Pkg->CurrentVer != 0 && P.InstallVer == 0) + iUsrSize -= Mult*Pkg.CurrentVer()->InstalledSize; +} + /*}}}*/ +// DepCache::AddStates - Add the package to the state counter /*{{{*/ +// --------------------------------------------------------------------- +/* This routine is tricky to use, you must make sure that it is never + called twice for the same package. This means the Remove/Add section + should be as short as possible and not encompass any code that will + calld Remove/Add itself. Remember, dependencies can be circular so + while processing a dep for Pkg it is possible that Add/Remove + will be called on Pkg */ +void pkgDepCache::AddStates(const PkgIterator &Pkg,int Add) +{ + StateCache &State = PkgState[Pkg->ID]; + + // The Package is broken + if ((State.DepState & DepInstMin) != DepInstMin) + iBrokenCount += Add; + + // Bad state + if (Pkg.State() != PkgIterator::NeedsNothing) + iBadCount += Add; + + // Not installed + if (Pkg->CurrentVer == 0) + { + if (State.Mode == ModeInstall) + iInstCount += Add; + return; + } + + // Installed, no upgrade + if (State.Upgradable() == false) + { + if (State.Mode == ModeDelete) + iDelCount += Add; + return; + } + + // Alll 3 are possible + if (State.Mode == ModeDelete) + iDelCount += Add; + if (State.Mode == ModeKeep) + iKeepCount += Add; + if (State.Mode == ModeInstall) + iInstCount += Add; +} + /*}}}*/ +// DepCache::BuildGroupOrs - Generate the Or group dep data /*{{{*/ +// --------------------------------------------------------------------- +/* The or group results are stored in the last item of the or group. This + allows easy detection of the state of a whole or'd group. */ +void pkgDepCache::BuildGroupOrs(VerIterator const &V) +{ + unsigned char Group = 0; + + for (DepIterator D = V.DependsList(); D.end() != true; D++) + { + // Build the dependency state. + unsigned char &State = DepState[D->ID]; + + /* Invert for Conflicts. We have to do this twice to get the + right sense for a conflicts group */ + if (D->Type == Dep::Conflicts) + State = ~State; + + // Add to the group if we are within an or.. + Group |= State; + State |= Group << 3; + if ((D->CompareOp & Dep::Or) != Dep::Or) + Group = 0; + + // Invert for Conflicts + if (D->Type == Dep::Conflicts) + State = ~State; + } +} + /*}}}*/ +// DepCache::VersionState - Perform a pass over a dependency list /*{{{*/ +// --------------------------------------------------------------------- +/* This is used to run over a dependency list and determine the dep + state of the list, filtering it through both a Min check and a Policy + check. The return result will have SetMin/SetPolicy low if a check + fails. It uses the DepState cache for it's computations. */ +unsigned char pkgDepCache::VersionState(DepIterator D,unsigned char Check, + unsigned char SetMin, + unsigned char SetPolicy) +{ + unsigned char Dep = 0xFF; + + while (D.end() != true) + { + // Compute a single dependency element (glob or) + DepIterator Start = D; + unsigned char State = 0; + for (bool LastOR = true; D.end() == false && LastOR == true; D++) + { + State |= DepState[D->ID]; + LastOR = (D->CompareOp & Dep::Or) == Dep::Or; + } + + // Minimum deps that must be satisfied to have a working package + if (Start.IsCritical() == true) + if ((State & Check) != Check) + Dep &= ~SetMin; + + // Policy deps that must be satisfied to install the package + if (IsImportantDep(Start) == true && + (State & Check) != Check) + Dep &= ~SetPolicy; + } + + return Dep; +} + /*}}}*/ +// DepCache::DependencyState - Compute the 3 results for a dep /*{{{*/ +// --------------------------------------------------------------------- +/* This is the main dependency computation bit. It computes the 3 main + results for a dependencys, Now, Install and Candidate. Callers must + invert the result if dealing with conflicts. */ +unsigned char pkgDepCache::DependencyState(DepIterator &D) +{ + unsigned char State = 0; + + if (CheckDep(D,NowVersion) == true) + State |= DepNow; + if (CheckDep(D,InstallVersion) == true) + State |= DepInstall; + if (CheckDep(D,CandidateVersion) == true) + State |= DepCVer; + + return State; +} + /*}}}*/ +// DepCache::UpdateVerState - Compute the Dep member of the state /*{{{*/ +// --------------------------------------------------------------------- +/* This determines the combined dependency representation of a package + for its two states now and install. This is done by using the pre-generated + dependency information. */ +void pkgDepCache::UpdateVerState(PkgIterator Pkg) +{ + // Empty deps are always true + StateCache &State = PkgState[Pkg->ID]; + State.DepState = 0xFF; + + // Check the Current state + if (Pkg->CurrentVer != 0) + { + DepIterator D = Pkg.CurrentVer().DependsList(); + State.DepState &= VersionState(D,DepNow,DepNowMin,DepNowPolicy); + } + + /* Check the candidate state. We do not compare against the whole as + a candidate state but check the candidate version against the + install states */ + if (State.CandidateVer != 0) + { + DepIterator D = State.CandidateVerIter(*this).DependsList(); + State.DepState &= VersionState(D,DepInstall,DepCandMin,DepCandPolicy); + } + + // Check target state which can only be current or installed + if (State.InstallVer != 0) + { + DepIterator D = State.InstVerIter(*this).DependsList(); + State.DepState &= VersionState(D,DepInstall,DepInstMin,DepInstPolicy); + } +} + /*}}}*/ +// DepCache::Update - Figure out all the state information /*{{{*/ +// --------------------------------------------------------------------- +/* This will figure out the state of all the packages and all the + dependencies based on the current policy. */ +void pkgDepCache::Update() +{ + iUsrSize = 0; + iDownloadSize = 0; + iDelCount = 0; + iInstCount = 0; + iKeepCount = 0; + iBrokenCount = 0; + iBadCount = 0; + + // Perform the depends pass + for (PkgIterator I = PkgBegin(); I.end() != true; I++) + { + for (VerIterator V = I.VersionList(); V.end() != true; V++) + { + unsigned char Group = 0; + + for (DepIterator D = V.DependsList(); D.end() != true; D++) + { + // Build the dependency state. + unsigned char &State = DepState[D->ID]; + State = DependencyState(D);; + + // Add to the group if we are within an or.. + Group |= State; + State |= Group << 3; + if ((D->CompareOp & Dep::Or) != Dep::Or) + Group = 0; + + // Invert for Conflicts + if (D->Type == Dep::Conflicts) + State = ~State; + } + } + + // Compute the pacakge dependency state and size additions + AddSizes(I); + UpdateVerState(I); + AddStates(I); + } +} + /*}}}*/ +// DepCache::Update - Update the deps list of a package /*{{{*/ +// --------------------------------------------------------------------- +/* This is a helper for update that only does the dep portion of the scan. + It is mainly ment to scan reverse dependencies. */ +void pkgDepCache::Update(DepIterator D) +{ + // Update the reverse deps + for (;D.end() != true; D++) + { + unsigned char &State = DepState[D->ID]; + State = DependencyState(D); + + // Invert for Conflicts + if (D->Type == Dep::Conflicts) + State = ~State; + + RemoveStates(D.ParentPkg()); + BuildGroupOrs(D.ParentVer()); + UpdateVerState(D.ParentPkg()); + AddStates(D.ParentPkg()); + } +} + /*}}}*/ +// DepCache::Update - Update the related deps of a package /*{{{*/ +// --------------------------------------------------------------------- +/* This is called whenever the state of a package changes. It updates + all cached dependencies related to this package. */ +void pkgDepCache::Update(PkgIterator const &Pkg) +{ + // Recompute the dep of the package + RemoveStates(Pkg); + UpdateVerState(Pkg); + AddStates(Pkg); + + // Update the reverse deps + Update(Pkg.RevDependsList()); + + // Update the provides map for the current ver + if (Pkg->CurrentVer != 0) + for (PrvIterator P = Pkg.CurrentVer().ProvidesList(); + P.end() != true; P++) + Update(P.ParentPkg().RevDependsList()); + + // Update the provides map for the candidate ver + for (PrvIterator P = PkgState[Pkg->ID].CandidateVerIter(*this).ProvidesList(); + P.end() != true; P++) + Update(P.ParentPkg().RevDependsList()); +} + + /*}}}*/ + +// DepCache::MarkKeep - Put the package in the keep state /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) +{ + // Simplifies other routines. + if (Pkg.end() == true) + return; + + /* We changed the soft state all the time so the UI is a bit nicer + to use */ + StateCache &P = PkgState[Pkg->ID]; + if (Soft == true) + P.iFlags |= AutoKept; + else + P.iFlags &= ~AutoKept; + + // Check that it is not already kept + if (P.Mode == ModeKeep) + return; + + // We dont even try to keep virtual packages.. + if (Pkg->VersionList == 0) + return; + + P.Flags &= ~Flag::Auto; + RemoveSizes(Pkg); + RemoveStates(Pkg); + + P.Mode = ModeKeep; + if (Pkg->CurrentVer == 0) + P.InstallVer = 0; + else + P.InstallVer = Pkg.CurrentVer(); + + AddStates(Pkg); + + Update(Pkg); + + AddSizes(Pkg); +} + /*}}}*/ +// DepCache::MarkDelete - Put the package in the delete state /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgDepCache::MarkDelete(PkgIterator const &Pkg) +{ + // Simplifies other routines. + if (Pkg.end() == true) + return; + + // Check that it is not already marked for delete + StateCache &P = PkgState[Pkg->ID]; + P.iFlags &= ~AutoKept; + if (P.Mode == ModeDelete || P.InstallVer == 0) + return; + + // We dont even try to delete virtual packages.. + if (Pkg->VersionList == 0) + return; + + RemoveSizes(Pkg); + RemoveStates(Pkg); + + P.Mode = ModeDelete; + P.InstallVer = 0; + P.Flags &= Flag::Auto; + + AddStates(Pkg); + Update(Pkg); + AddSizes(Pkg); +} + /*}}}*/ +// DepCache::MarkInstall - Put the package in the install state /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst) +{ + // Simplifies other routines. + if (Pkg.end() == true) + return; + + /* Check that it is not already marked for install and that it can be + installed */ + StateCache &P = PkgState[Pkg->ID]; + P.iFlags &= ~AutoKept; + if (P.InstBroken() == false && (P.Mode == ModeInstall || + P.CandidateVer == (Version *)Pkg.CurrentVer())) + { + if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0) + MarkKeep(Pkg); + return; + } + + // We dont even try to install virtual packages.. + if (Pkg->VersionList == 0) + return; + + /* Target the candidate version and remove the autoflag. We reset the + autoflag below if this was called recursively. Otherwise the user + should have the ability to de-auto a package by changing its state */ + RemoveSizes(Pkg); + RemoveStates(Pkg); + + P.Mode = ModeInstall; + P.InstallVer = P.CandidateVer; + P.Flags &= ~Flag::Auto; + if (P.CandidateVer == (Version *)Pkg.CurrentVer()) + P.Mode = ModeKeep; + + AddStates(Pkg); + Update(Pkg); + AddSizes(Pkg); + + if (AutoInst == false) + return; + + DepIterator Dep = P.InstVerIter(*this).DependsList(); + for (; Dep.end() != true;) + { + // Grok or groups + DepIterator Start = Dep; + bool Result = true; + for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++) + { + LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or; + + if ((DepState[Dep->ID] & DepInstall) == DepInstall) + Result = false; + } + + // Dep is satisfied okay. + if (Result == false) + continue; + + /* Check if this dep should be consider for install. If it is a user + defined important dep and we are installed a new package then + it will be installed. Otherwise we only worry about critical deps */ + if (IsImportantDep(Start) == false) + continue; + if (Pkg->CurrentVer != 0 && Start.IsCritical() == false) + continue; + + // Now we have to take action... + PkgIterator P = Start.SmartTargetPkg(); + if ((DepState[Start->ID] & DepCVer) == DepCVer) + { + MarkInstall(P,true); + + // Set the autoflag, after MarkInstall because MarkInstall unsets it + if (P->CurrentVer == 0) + PkgState[P->ID].Flags |= Flag::Auto; + + continue; + } + + // For conflicts we just de-install the package and mark as auto + if (Start->Type == Dep::Conflicts) + { + Version **List = Start.AllTargets(); + for (Version **I = List; *I != 0; I++) + { + VerIterator Ver(*this,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + MarkDelete(Pkg); + PkgState[Pkg->ID].Flags |= Flag::Auto; + } + delete [] List; + continue; + } + } +} + /*}}}*/ + +#if 0 +// DepCache::ResolveConflicts - Figure the auto upgrades /*{{{*/ +// --------------------------------------------------------------------- +/* This routine attempts to resolve conflicts generated by automatic + upgrading. It is known as 'Stage 1' but that name isn't as proper anymore. + + It's most important function is during the initial load of APT. The + loading code will mark every package for upgrade to it's candidate version + and then call this routine. This routine will then 'soft keep' every + package that causes conflict, is conflicted, or so on. It is a bit + agressive in that it may unselect more packages in some odd cases + than are strictly necessary but in the case where no packages were + conflicting before it will always produce a system with no packages + conflicting after. + + This routine is also used during state changes that require autoupgrade + scanning. That is, if a new package is marked for install then all packages + that have been soft kept are reconsidered for upgrade. + + It is called with state information about what can be un-upgraded and + what the pre-upgrade install state was. It is expected that the caller + has already marked the desired packages to the install state. Bit 0 is + the original install state and Bit 1 is controls whether the package + should be touched. +*/ +void pkgDepCache::ResolveConflicts(unsigned char *Touched) +{ + bool Again = false; + do + { + Again = false; + for (PkgIterator I = PkgBegin(); I.end() != true; I++) + { + // The package will install OK + if ((PkgState[I->ID].DepState & DepInstMin) == DepInstMin) + continue; + + /* The package was broken before and this upgrade will not + make things better. We simply mark the package for keep + and assume an upgrade attempt will be hopeless. This might + not be ideal. */ + if ((Touched[I->ID] & (1 << 0)) != (1 << 0)) + { + // The package isnt to be touched + if ((Touched[I->ID] & (1 << 1)) == (1 << 1)) + MarkKeep(I,true); + + continue; + } + + // Check to see if not upgrading it will solve the problem + if (I->CurrentVer != 0) + { + // The package isnt to be touched + if ((Touched[I->ID] & (1 << 1)) == (1 << 1)) + { + if (PkgState[I->ID].Mode != ModeKeep) + Again = true; + + MarkKeep(I,true); + } + + /* Check if the package is sill broken. If so then we cant leave + it as is and get a working system. Lets try marking some + depends for 'keep'. This is brutal, it keeps everything in + sight to fix the problem. */ + DepIterator D = I.CurrentVer().DependsList(); + for (;(PkgState[I->ID].DepState & DepInstMin) != DepInstMin && + D.end() != true; D++) + { + // We only worry about critical deps. + if (D.IsCritical() != true) + continue; + + unsigned char State = DepState[D->ID]; + + // This dep never was set before so we dont need to set it now + if ((State & DepNow) != DepNow) + continue; + + // The dep is okay now no worries. + if ((State & DepInstall) == DepInstall) + continue; + + // Locate a target to keep + PkgIterator P(*this); + if (CheckDep(D,NowVersion,P) == true) + { + // We cant touch this package + if ((Touched[P->ID] & (1 << 1)) == (1 << 1)) + MarkKeep(P,true); + } + } + } + } + } + while (Again == true); +} + /*}}}*/ +// DepCache::PromoteAutoKeep - Gentler version of the above /*{{{*/ +// --------------------------------------------------------------------- +/* This is used when installing packages, all it does is attempt to promote + everything that has been auto-kept. It simply promotes everything + irregardless of it having a chance to work and then runs ResolveConflicts + on the result. This allows circular depends loops to work but isn't + terribly fast. */ +void pkgDepCache::PromoteAutoKeep() +{ + /* Initialize the touchd array. Bit 0 is the old install state bit 1 + is the touch value */ + unsigned char *Touch = new unsigned char[Head().PackageCount]; + for (unsigned int I = 0; I != Head().PackageCount; I++) + { + if ((PkgState[I].DepState & DepInstMin) == DepInstMin) + Touch[I] = 1 << 0; + else + Touch[I] = 0; + } + + // This allows circular depends to work + for (PkgIterator I = PkgBegin(); I.end() != true; I++) + { + /* It wasnt installed before or it is not autokept or it is not + upgradeable */ + StateCache &P = PkgState[I->ID]; + if (I->CurrentVer == 0 || P.Mode != ModeKeep || I->VersionList == 0 || + P.CandidateVer == (Version *)I.CurrentVer() || + (P.iFlags & AutoKept) != AutoKept) + continue; + + P.Mode = ModeInstall; + P.InstallVer = P.CandidateVer; + if (P.CandidateVer == (Version *)I.CurrentVer()) + P.Mode = ModeKeep; + + // Okay autoupgrade it. + Touch[I->ID] |= 1 << 1; + } + + Update(); + ResolveConflicts(Touch); + + delete [] Touch; +} + /*}}}*/ +// DepCache::AllUpgrade - Try to upgrade everything /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgDepCache::AllUpgrade() +{ + // Set everything to an upgrade mode + for (PkgIterator I = PkgBegin(); I.end() != true; I++) + { + StateCache &State = PkgState[I->ID]; + + /* We dont upgrade packages marked for deletion or that are + not installed or that don't have an upgrade */ + if (State.Mode == ModeDelete || I->CurrentVer == 0 || + (Version *)I.CurrentVer() == State.CandidateVer) + continue; + + // Set the state to upgrade + State.iFlags = 0; + State.Mode = ModeInstall; + State.InstallVer = State.CandidateVer; + if (State.CandidateVer == (Version *)I.CurrentVer()) + State.Mode = ModeKeep; + + // Do not upgrade things that have the hold flag set. + if (I->SelectedState == State::Hold) + { + State.InstallVer = I.CurrentVer(); + State.Mode = ModeKeep; + } + State.Update(I,*this); + } + + Update(); + + /* Initialize the touchd array. Bit 0 is the old install state bit 1 + is the touch value */ + unsigned char *Touch = new unsigned char[Head().PackageCount]; + for (unsigned int I = 0; I != Head().PackageCount; I++) + { + if ((PkgState[I].DepState & DepNowMin) == DepNowMin) + Touch[I] = (1 << 0) | (1 << 1); + else + Touch[I] = 1 << 1; + } + + // Now downgrade everything that is broken + ResolveConflicts(Touch); + delete [] Touch; +} + /*}}}*/ +#endif + +// StateCache::Update - Compute the various static display things /*{{{*/ +// --------------------------------------------------------------------- +/* This is called whenever the Candidate version changes. */ +void pkgDepCache::StateCache::Update(PkgIterator Pkg,pkgCache &Cache) +{ + // Some info + VerIterator Ver = CandidateVerIter(Cache); + + // Use a null string or the version string + if (Ver.end() == true) + CandVersion = ""; + else + CandVersion = Ver.VerStr(); + + // Find the current version + CurVersion = ""; + if (Pkg->CurrentVer != 0) + CurVersion = Pkg.CurrentVer().VerStr(); + + // Strip off the epochs for display + CurVersion = StripEpoch(CurVersion); + CandVersion = StripEpoch(CandVersion); + + // Figure out if its up or down or equal + Status = Ver.CompareVer(Pkg.CurrentVer()); + if (Pkg->CurrentVer == 0 || Pkg->VersionList == 0 || CandidateVer == 0) + Status = 2; +} + /*}}}*/ +// StateCache::StripEpoch - Remove the epoch specifier from the version /*{{{*/ +// --------------------------------------------------------------------- +/* */ +const char *pkgDepCache::StateCache::StripEpoch(const char *Ver) +{ + if (Ver == 0) + return 0; + + // Strip any epoch + for (const char *I = Ver; *I != 0; I++) + if (*I == ':') + return I + 1; + return Ver; +} + /*}}}*/ diff --git a/apt-pkg/depcache.h b/apt-pkg/depcache.h new file mode 100644 index 000000000..222e34425 --- /dev/null +++ b/apt-pkg/depcache.h @@ -0,0 +1,189 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: depcache.h,v 1.1 1998/07/07 04:17:01 jgg Exp $ +/* ###################################################################### + + DepCache - Dependency Extension data for the cache + + This class stores the cache data and a set of extension structures for + monitoring the current state of all the packages. It also generates and + caches the 'install' state of many things. This refers to the state of the + package after an install has been run. + + The StateCache::State field can be -1,0,1,2 which is <,=,>,no current. + StateCache::Mode is which of the 3 fields is active. + + This structure is important to support the readonly status of the cache + file. When the data is saved the cache will be refereshed from our + internal rep and written to disk. Then the actual persistant data + files will be put on the disk. + + Each dependency is compared against 3 target versions to produce to + 3 dependency results. + Now - Compared using the Currently install version + Install - Compared using the install version (final state) + CVer - (Candidate Verion) Compared using the Candidate Version + The candidate and now results are used to decide wheather a package + should be automatically installed or if it should be left alone. + + Remember, the Candidate Version is selected based on the distribution + settings for the Package. The Install Version is selected based on the + state (Delete, Keep, Install) field and can be either the Current Version + or the Candidate version. + + The Candidate version is what is shown the 'Install Version' field. + + ##################################################################### */ + /*}}}*/ +// Header section: pkglib +#ifndef PKGLIB_DEPCACHE_H +#define PKGLIB_DEPCACHE_H + +#ifdef __GNUG__ +#pragma interface "pkglib/depcache.h" +#endif + +#include + +class pkgDepCache : public pkgCache +{ + public: + + // These flags are used in DepState + enum DepFlags {DepNow = (1 << 0),DepInstall = (1 << 1),DepCVer = (1 << 2), + DepGNow = (1 << 3),DepGInstall = (1 << 4),DepGCVer = (1 << 5)}; + + // These flags are used in StateCache::DepState + enum DepStateFlags {DepNowPolicy = (1 << 0), DepNowMin = (1 << 1), + DepInstPolicy = (1 << 2), DepInstMin = (1 << 3), + DepCandPolicy = (1 << 4), DepCandMin = (1 << 5)}; + + // These flags are used in StateCache::iFlags + enum InternalFlags {AutoKept = (1 << 0)}; + + enum VersionTypes {NowVersion, InstallVersion, CandidateVersion}; + enum ModeList {ModeDelete = 0, ModeKeep = 1, ModeInstall = 2}; + struct StateCache + { + // Epoch stripped text versions of the two version fields + const char *CandVersion; + const char *CurVersion; + + // Pointer to the candidate install version. + Version *CandidateVer; + + // Pointer to the install version. + Version *InstallVer; + + // Various tree indicators + signed char Status; // -1,0,1,2 + unsigned char Mode; // ModeList + unsigned char DepState; // DepState Flags + + // Copy of Package::Flags + unsigned short Flags; + unsigned short iFlags; // Internal flags + + // Update of candidate version + const char *StripEpoch(const char *Ver); + void Update(PkgIterator Pkg,pkgCache &Cache); + + // Various test members for the current status of the package + inline bool NewInstall() const {return Status == 2 && Mode == ModeInstall;}; + inline bool Delete() const {return Mode == ModeDelete;}; + inline bool Keep() const {return Mode == ModeKeep;}; + inline bool Upgrade() const {return Status > 0 && Mode == ModeInstall;}; + inline bool Upgradable() const {return Status == 1;}; + inline bool Downgrade() const {return Status < 0;}; + inline bool Held() const {return Status != 0 && Keep();}; + inline bool NowBroken() const {return (DepState & DepNowMin) != DepNowMin;}; + inline bool InstBroken() const {return (DepState & DepInstMin) != DepInstMin;}; + inline bool Install() const {return Mode == ModeInstall;}; + inline VerIterator InstVerIter(pkgCache &Cache) + {return VerIterator(Cache,InstallVer);}; + inline VerIterator CandidateVerIter(pkgCache &Cache) + {return VerIterator(Cache,CandidateVer);}; + }; + + // Helper functions + void BuildGroupOrs(VerIterator const &V); + void UpdateVerState(PkgIterator Pkg); + + bool Init(); + + protected: + + // State information + StateCache *PkgState; + unsigned char *DepState; + + long iUsrSize; + long iDownloadSize; + long iInstCount; + long iDelCount; + long iKeepCount; + long iBrokenCount; + long iBadCount; + + // Check for a matching provides + bool CheckDep(DepIterator Dep,int Type,PkgIterator &Res); + inline bool CheckDep(DepIterator Dep,int Type) + { + PkgIterator Res(*this); + return CheckDep(Dep,Type,Res); + } + + // Computes state information for deps and versions (w/o storing) + unsigned char DependencyState(DepIterator &D); + unsigned char VersionState(DepIterator D,unsigned char Check, + unsigned char SetMin, + unsigned char SetPolicy); + + // Recalculates various portions of the cache, call after changing something + void Update(DepIterator Dep); // Mostly internal + void Update(PkgIterator const &P); + + // Count manipulators + void AddSizes(const PkgIterator &Pkg,long Mult = 1); + inline void RemoveSizes(const PkgIterator &Pkg) {AddSizes(Pkg,-1);}; + void AddStates(const PkgIterator &Pkg,int Add = 1); + inline void RemoveStates(const PkgIterator &Pkg) {AddStates(Pkg,-1);}; + + public: + + // Policy implementation + virtual VerIterator GetCandidateVer(PkgIterator Pkg); + virtual bool IsImportantDep(DepIterator Dep); + + // Accessors + inline StateCache &operator [](PkgIterator const &I) {return PkgState[I->ID];}; + inline unsigned char &operator [](DepIterator const &I) {return DepState[I->ID];}; + + // Manipulators + void MarkKeep(PkgIterator const &Pkg,bool Soft = false); + void MarkDelete(PkgIterator const &Pkg); + void MarkInstall(PkgIterator const &Pkg,bool AutoInst = true); + + // This is for debuging + void Update(); + + // Dep Processing for AutoKeep + void ResolveConflicts(unsigned char *Touched); + + // Hook to keep the extra data in sync + virtual bool ReMap(); + + // Size queries + inline long UsrSize() {return iUsrSize;}; + inline long DebSize() {return iDownloadSize;}; + inline long DelCount() {return iDelCount;}; + inline long KeepCount() {return iKeepCount;}; + inline long InstCount() {return iInstCount;}; + inline long BrokenCount() {return iBrokenCount;}; + inline long BadCount() {return iBadCount;}; + + pkgDepCache(MMap &Map); + virtual ~pkgDepCache(); +}; + +#endif diff --git a/apt-pkg/orderlist.cc b/apt-pkg/orderlist.cc new file mode 100644 index 000000000..f79a063bd --- /dev/null +++ b/apt-pkg/orderlist.cc @@ -0,0 +1,829 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: orderlist.cc,v 1.1 1998/07/07 04:17:01 jgg Exp $ +/* ###################################################################### + + Order List - Represents and Manipulates an ordered list of packages. + + A list of packages can be ordered by a number of conflicting criteria + each given a specific priority. Each package also has a set of flags + indicating some usefull things about it that are derived in the + course of sorting. The pkgPackageManager class uses this class for + all of it's installation ordering needs. + + This is a modified version of Manoj's Routine B. It consists of four + independent ordering algorithms that can be applied at for different + points in the ordering. By appling progressivly fewer ordering + operations it is possible to give each consideration it's own + priority and create an order that satisfies the lowest applicable + consideration. + + The rules for unpacking ordering are: + 1) Unpacking ignores Depends: on all packages + 2) Unpacking requires Conflicts: on -ALL- packages to be satisfied + 3) Unpacking requires PreDepends: on this package only to be satisfied + 4) Removing requires that no packages depend on the package to be + removed. + + And the rule for configuration ordering is: + 1) Configuring requires that the Depends: of the package be satisfied + Conflicts+PreDepends are ignored because unpacking says they are + already correct [exageration, it does check but we need not be + concerned] + + And some features that are valuable for unpacking ordering. + f1) Unpacking a new package should advoid breaking dependencies of + configured packages + f2) Removal should not require a force, corrolory of f1 + f3) Unpacking should order by depends rather than fall back to random + ordering. + + Each of the features can be enabled in the sorting routine at an + arbitary priority to give quite abit of control over the final unpacking + order. + + The rules listed above my never be violated and are called Critical. + When a critical rule is violated then a loop condition is recorded + and will have to be delt with in the caller. + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/orderlist.h" +#endif +#include +#include +#include +#include + /*}}}*/ + +pkgOrderList *pkgOrderList::Me = 0; + +// OrderList::pkgOrderList - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgOrderList::pkgOrderList(pkgDepCache &Cache) : Cache(Cache) +{ + Primary = 0; + Secondary = 0; + RevDepends = 0; + Remove = 0; + LoopCount = -1; + + /* Construct the arrays, egcs 1.0.1 bug requires the package count + hack */ + unsigned long Size = Cache.HeaderP->PackageCount; + Flags = new unsigned char[Size]; + End = List = new Package *[Size]; + memset(Flags,0,sizeof(*Flags)*Size); +} + /*}}}*/ +// OrderList::~pkgOrderList - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgOrderList::~pkgOrderList() +{ + delete [] List; + delete [] Flags; +} + /*}}}*/ + +// OrderList::DoRun - Does an order run /*{{{*/ +// --------------------------------------------------------------------- +/* The caller is expeted to have setup the desired probe state */ +bool pkgOrderList::DoRun() +{ + // Temp list + unsigned long Size = Cache.HeaderP->PackageCount; + Package **NList = new Package *[Size]; + + Depth = 0; + WipeFlags(Added | AddPending | Loop | InList); + + for (iterator I = List; I != End; I++) + Flag(*I,InList); + + // Rebuild the main list into the temp list. + iterator OldEnd = End; + End = NList; + for (iterator I = List; I != OldEnd; I++) + if (VisitNode(PkgIterator(Cache,*I)) == false) + { + End = OldEnd; + delete [] NList; + return false; + } + + // Swap the main list to the new list + delete [] List; + List = NList; + return true; +} + /*}}}*/ +// OrderList::OrderCritical - Perform critical unpacking ordering /*{{{*/ +// --------------------------------------------------------------------- +/* This performs predepends and immediate configuration ordering only. + This is termed critical unpacking ordering. Any loops that form are + fatal and indicate that the packages cannot be installed. */ +bool pkgOrderList::OrderCritical() +{ + Primary = &DepUnPackPre; + Secondary = 0; + RevDepends = 0; + Remove = 0; + LoopCount = 0; + + // Sort + Me = this; + qsort(List,End - List,sizeof(*List),&OrderCompareB); + + if (DoRun() == false) + return false; + + if (LoopCount != 0) + return _error->Error("Fatal, predepends looping detected"); + return true; +} + /*}}}*/ +// OrderList::OrderUnpack - Perform complete unpacking ordering /*{{{*/ +// --------------------------------------------------------------------- +/* This performs complete unpacking ordering and creates an order that is + suitable for unpacking */ +bool pkgOrderList::OrderUnpack() +{ + Primary = &DepUnPackCrit; + Secondary = &DepConfigure; + RevDepends = &DepUnPackDep; + Remove = &DepRemove; + LoopCount = -1; + + // Sort + Me = this; + qsort(List,End - List,sizeof(*List),&OrderCompareA); + + if (DoRun() == false) + return false; + + Secondary = 0; + if (DoRun() == false) + return false; + + LoopCount = 0; + RevDepends = 0; + Remove = 0; // Otherwise the libreadline remove problem occures + if (DoRun() == false) + return false; + + LoopCount = 0; + Primary = &DepUnPackPre; + if (DoRun() == false) + return false; + +/* cout << "----------END" << endl; + + for (iterator I = List; I != End; I++) + { + PkgIterator P(Cache,*I); + cout << P.Name() << endl; + }*/ + + return true; +} + /*}}}*/ +// OrderList::OrderConfigure - Perform configuration ordering /*{{{*/ +// --------------------------------------------------------------------- +/* This orders by depends only and produces an order which is suitable + for configuration */ +bool pkgOrderList::OrderConfigure() +{ + Primary = &DepConfigure; + Secondary = 0; + RevDepends = 0; + Remove = 0; + LoopCount = -1; + return DoRun(); +} + /*}}}*/ + +// OrderList::Score - Score the package for sorting /*{{{*/ +// --------------------------------------------------------------------- +/* Higher scores order earlier */ +int pkgOrderList::Score(PkgIterator Pkg) +{ + // Removal is always done first + if (Cache[Pkg].Delete() == true) + return 200; + + int Score = 0; + if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) + Score += 100; + + for (DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); + D.end() == false; D++) + if (D->Type == pkgCache::Dep::PreDepends) + { + Score += 50; + break; + } + + // Important Required Standard Optional Extra + signed short PrioMap[] = {0,5,4,3,1,0}; + if (Cache[Pkg].InstVerIter(Cache)->Priority <= 5) + Score += PrioMap[Cache[Pkg].InstVerIter(Cache)->Priority]; + return Score; +} + /*}}}*/ +// OrderList::FileCmp - Compare by package file /*{{{*/ +// --------------------------------------------------------------------- +/* This compares by the package file that the install version is in. */ +int pkgOrderList::FileCmp(PkgIterator A,PkgIterator B) +{ + if (Cache[A].Delete() == true && Cache[B].Delete() == true) + return 0; + if (Cache[A].Delete() == true) + return -1; + if (Cache[B].Delete() == true) + return 1; + + if (Cache[A].InstVerIter(Cache).FileList().end() == true) + return -1; + if (Cache[A].InstVerIter(Cache).FileList().end() == true) + return 1; + + pkgCache::PackageFile *FA = Cache[A].InstVerIter(Cache).FileList().File(); + pkgCache::PackageFile *FB = Cache[B].InstVerIter(Cache).FileList().File(); + if (FA < FB) + return -1; + if (FA > FB) + return 1; + return 0; +} + /*}}}*/ +// OrderList::OrderCompareA - Order the installation by op /*{{{*/ +// --------------------------------------------------------------------- +/* This provides a first-pass sort of the list and gives a decent starting + point for further complete ordering. It is used by OrderUnpack only */ +int pkgOrderList::OrderCompareA(const void *a, const void *b) +{ + PkgIterator A(Me->Cache,*(Package **)a); + PkgIterator B(Me->Cache,*(Package **)b); + + if (A.State() != pkgCache::PkgIterator::NeedsNothing && + B.State() == pkgCache::PkgIterator::NeedsNothing) + return -1; + + if (A.State() == pkgCache::PkgIterator::NeedsNothing && + B.State() != pkgCache::PkgIterator::NeedsNothing) + return 1; + + int ScoreA = Me->Score(A); + int ScoreB = Me->Score(B); + if (ScoreA > ScoreB) + return -1; + + if (ScoreA < ScoreB) + return 1; + + return strcmp(A.Name(),B.Name()); +} + /*}}}*/ +// OrderList::OrderCompareB - Order the installation by source /*{{{*/ +// --------------------------------------------------------------------- +/* This orders by installation source. This is usefull to handle + inter-source breaks */ +int pkgOrderList::OrderCompareB(const void *a, const void *b) +{ + PkgIterator A(Me->Cache,*(Package **)a); + PkgIterator B(Me->Cache,*(Package **)b); + + if (A.State() != pkgCache::PkgIterator::NeedsNothing && + B.State() == pkgCache::PkgIterator::NeedsNothing) + return -1; + + if (A.State() == pkgCache::PkgIterator::NeedsNothing && + B.State() != pkgCache::PkgIterator::NeedsNothing) + return 1; + + int F = Me->FileCmp(A,B); + if (F != 0) + { + if (F > 0) + return -1; + return 1; + } + + int ScoreA = Me->Score(A); + int ScoreB = Me->Score(B); + if (ScoreA > ScoreB) + return -1; + + if (ScoreA < ScoreB) + return 1; + + return strcmp(A.Name(),B.Name()); +} + /*}}}*/ + +// OrderList::VisitDeps - Visit forward install dependencies /*{{{*/ +// --------------------------------------------------------------------- +/* This calls the dependency function for the normal forwards dependencies + of the package */ +bool pkgOrderList::VisitDeps(DepFunc F,PkgIterator Pkg) +{ + if (F == 0 || Pkg.end() == true || Cache[Pkg].InstallVer == 0) + return true; + + return (this->*F)(Cache[Pkg].InstVerIter(Cache).DependsList()); +} + /*}}}*/ +// OrderList::VisitRDeps - Visit reverse dependencies /*{{{*/ +// --------------------------------------------------------------------- +/* This calls the dependency function for all of the normal reverse depends + of the package */ +bool pkgOrderList::VisitRDeps(DepFunc F,PkgIterator Pkg) +{ + if (F == 0 || Pkg.end() == true) + return true; + + return (this->*F)(Pkg.RevDependsList()); +} + /*}}}*/ +// OrderList::VisitRProvides - Visit provides reverse dependencies /*{{{*/ +// --------------------------------------------------------------------- +/* This calls the dependency function for all reverse dependencies + generated by the provides line on the package. */ +bool pkgOrderList::VisitRProvides(DepFunc F,VerIterator Ver) +{ + if (F == 0 || Ver.end() == true) + return true; + + bool Res = true; + for (PrvIterator P = Ver.ProvidesList(); P.end() == false; P++) + Res &= (this->*F)(P.ParentPkg().RevDependsList()); + return true; +} + /*}}}*/ +// OrderList::VisitProvides - Visit all of the providing packages /*{{{*/ +// --------------------------------------------------------------------- +/* This routine calls visit on all providing packages. */ +bool pkgOrderList::VisitProvides(DepIterator D) +{ + Version **List = D.AllTargets(); + for (Version **I = List; *I != 0; I++) + { + VerIterator Ver(Cache,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + if (Cache[Pkg].Keep() == true) + continue; + + if (D->Type != pkgCache::Dep::Conflicts && Cache[Pkg].InstallVer != *I) + continue; + + if (D->Type == pkgCache::Dep::Conflicts && (Version *)Pkg.CurrentVer() != *I) + continue; + + if (VisitNode(Pkg) == false) + { + delete [] List; + return false; + } + } + delete [] List; + return true; +} + /*}}}*/ +// OrderList::VisitNode - Recursive ordering director /*{{{*/ +// --------------------------------------------------------------------- +/* This is the core ordering routine. It calls the set dependency + consideration functions which then potentialy call this again. Finite + depth is achived through the colouring mechinism. */ +bool pkgOrderList::VisitNode(PkgIterator Pkg) +{ + // Looping or irrelevent. + if (Pkg.end() == true || IsFlag(Pkg,Added) == true || + IsFlag(Pkg,AddPending) == true || IsFlag(Pkg,InList) == false) + return true; + +/* for (int j = 0; j != Depth; j++) cout << ' '; + cout << "Visit " << Pkg.Name() << endl;*/ + Depth++; + + // Color grey + Flag(Pkg,AddPending); + + DepFunc Old = Primary; + + // Perform immedate configuration of the package if so flagged. + if (IsFlag(Pkg,Immediate) == true && Primary != &DepUnPackPre) + Primary = &DepUnPackPreD; + + bool Res = true; + if (Cache[Pkg].Delete() == false) + { + // Primary + Res &= Res && VisitDeps(Primary,Pkg); + Res &= Res && VisitRDeps(Primary,Pkg); + Res &= Res && VisitRProvides(Primary,Pkg.CurrentVer()); + Res &= Res && VisitRProvides(Primary,Cache[Pkg].InstVerIter(Cache)); + + // RevDep + Res &= Res && VisitRDeps(RevDepends,Pkg); + Res &= Res && VisitRProvides(RevDepends,Pkg.CurrentVer()); + Res &= Res && VisitRProvides(RevDepends,Cache[Pkg].InstVerIter(Cache)); + + // Secondary + Res &= Res && VisitDeps(Secondary,Pkg); + Res &= Res && VisitRDeps(Secondary,Pkg); + Res &= Res && VisitRProvides(Secondary,Pkg.CurrentVer()); + Res &= Res && VisitRProvides(Secondary,Cache[Pkg].InstVerIter(Cache)); + } + else + { + // RevDep + Res &= Res && VisitRDeps(Remove,Pkg); + Res &= Res && VisitRProvides(Remove,Pkg.CurrentVer()); + } + + if (IsFlag(Pkg,Added) == false) + { + Flag(Pkg,Added,Added | AddPending); + *End = Pkg; + End++; + } + + Primary = Old; + Depth--; + +/* for (int j = 0; j != Depth; j++) cout << ' '; + cout << "Leave " << Pkg.Name() << ' ' << IsFlag(Pkg,Added) << ',' << IsFlag(Pkg,AddPending) << endl;*/ + + return true; +} + /*}}}*/ + +// OrderList::DepUnPackCrit - Critical UnPacking ordering /*{{{*/ +// --------------------------------------------------------------------- +/* Critical unpacking ordering strives to satisfy Conflicts: and + PreDepends: only. When a prdepends is encountered the Primary + DepFunc is changed to be DepUnPackPreD. + + Loops are preprocessed and logged. */ +bool pkgOrderList::DepUnPackCrit(DepIterator D) +{ + for (; D.end() == false; D++) + { + if (D.Reverse() == true) + { + /* Reverse depenanices are only interested in conflicts, + predepend breakage is ignored here */ + if (D->Type != pkgCache::Dep::Conflicts) + continue; + + // Duplication elimination, consider only the current version + if (D.ParentPkg().CurrentVer() != D.ParentVer()) + continue; + + /* For reverse dependencies we wish to check if the + dependency is satisifed in the install state. The + target package (caller) is going to be in the installed + state. */ + if (CheckDep(D) == true) + continue; + + if (VisitNode(D.ParentPkg()) == false) + return false; + } + else + { + /* Forward critical dependencies MUST be correct before the + package can be unpacked. */ + if (D->Type != pkgCache::Dep::Conflicts && D->Type != pkgCache::Dep::PreDepends) + continue; + + /* We wish to check if the dep is okay in the now state of the + target package against the install state of this package. */ + if (CheckDep(D) == true) + { + /* We want to catch predepends loops with the code below. + Conflicts loops that are Dep OK are ignored */ + if (IsFlag(D.TargetPkg(),AddPending) == false || + D->Type != pkgCache::Dep::PreDepends) + continue; + } + + // This is the loop detection + if (IsFlag(D.TargetPkg(),Added) == true || + IsFlag(D.TargetPkg(),AddPending) == true) + { + if (IsFlag(D.TargetPkg(),AddPending) == true) + AddLoop(D); + continue; + } + + /* Predepends require a special ordering stage, they must have + all dependents installed as well */ + DepFunc Old = Primary; + bool Res = false; + if (D->Type == pkgCache::Dep::PreDepends) + Primary = &DepUnPackPreD; + Res = VisitProvides(D); + Primary = Old; + if (Res == false) + return false; + } + } + return true; +} + /*}}}*/ +// OrderList::DepUnPackPreD - Critical UnPacking ordering with depends /*{{{*/ +// --------------------------------------------------------------------- +/* Critical PreDepends (also configure immediate and essential) strives to + ensure not only that all conflicts+predepends are met but that this + package will be immediately configurable when it is unpacked. + + Loops are preprocessed and logged. */ +bool pkgOrderList::DepUnPackPreD(DepIterator D) +{ + if (D.Reverse() == true) + return DepUnPackCrit(D); + + for (; D.end() == false; D++) + { + if (D.IsCritical() == false) + continue; + + /* We wish to check if the dep is okay in the now state of the + target package against the install state of this package. */ + if (CheckDep(D) == true) + { + /* We want to catch predepends loops with the code below. + Conflicts loops that are Dep OK are ignored */ + if (IsFlag(D.TargetPkg(),AddPending) == false || + D->Type != pkgCache::Dep::PreDepends) + continue; + } + + // This is the loop detection + if (IsFlag(D.TargetPkg(),Added) == true || + IsFlag(D.TargetPkg(),AddPending) == true) + { + if (IsFlag(D.TargetPkg(),AddPending) == true) + AddLoop(D); + continue; + } + + if (VisitProvides(D) == false) + return false; + } + return true; +} + /*}}}*/ +// OrderList::DepUnPackPre - Critical Predepends ordering /*{{{*/ +// --------------------------------------------------------------------- +/* Critical PreDepends (also configure immediate and essential) strives to + ensure not only that all conflicts+predepends are met but that this + package will be immediately configurable when it is unpacked. + + Loops are preprocessed and logged. All loops will be fatal. */ +bool pkgOrderList::DepUnPackPre(DepIterator D) +{ + if (D.Reverse() == true) + return true; + + for (; D.end() == false; D++) + { + /* Only consider the PreDepends or Depends. Depends are only + considered at the lowest depth or in the case of immediate + configure */ + if (D->Type != pkgCache::Dep::PreDepends) + { + if (D->Type == pkgCache::Dep::Depends) + { + if (Depth == 1 && IsFlag(D.ParentPkg(),Immediate) == false) + continue; + } + else + continue; + } + + /* We wish to check if the dep is okay in the now state of the + target package against the install state of this package. */ + if (CheckDep(D) == true) + { + /* We want to catch predepends loops with the code below. + Conflicts loops that are Dep OK are ignored */ + if (IsFlag(D.TargetPkg(),AddPending) == false) + continue; + } + + // This is the loop detection + if (IsFlag(D.TargetPkg(),Added) == true || + IsFlag(D.TargetPkg(),AddPending) == true) + { + if (IsFlag(D.TargetPkg(),AddPending) == true) + AddLoop(D); + continue; + } + + if (VisitProvides(D) == false) + return false; + } + return true; +} + /*}}}*/ +// OrderList::DepUnPackDep - Reverse dependency considerations /*{{{*/ +// --------------------------------------------------------------------- +/* Reverse dependencies are considered to determine if unpacking this + package will break any existing dependencies. If so then those + packages are ordered before this one so that they are in the + UnPacked state. + + The forwards depends loop is designed to bring the packages dependents + close to the package. This helps reduce deconfigure time. + + Loops are irrelevent to this. */ +bool pkgOrderList::DepUnPackDep(DepIterator D) +{ + + for (; D.end() == false; D++) + if (D.IsCritical() == true) + { + if (D.Reverse() == true) + { + /* Duplication prevention. We consider rev deps only on + the current version, a not installed package + cannot break */ + if (D.ParentPkg()->CurrentVer == 0 || + D.ParentPkg().CurrentVer() != D.ParentVer()) + continue; + + // The dep will not break so it is irrelevent. + if (CheckDep(D) == true) + continue; + + if (VisitNode(D.ParentPkg()) == false) + return false; + } + else + if (D->Type == pkgCache::Dep::Depends) + if (VisitProvides(D) == false) + return false; + } + return true; +} + /*}}}*/ +// OrderList::DepConfigure - Configuration ordering /*{{{*/ +// --------------------------------------------------------------------- +/* Configuration only ordering orders by the Depends: line only. It + orders configuration so that when a package comes to be configured it's + dependents are configured. + + Loops are ingored. Depends loop entry points are chaotic. */ +bool pkgOrderList::DepConfigure(DepIterator D) +{ + // Never consider reverse configuration dependencies. + if (D.Reverse() == true) + return true; + + for (; D.end() == false; D++) + if (D->Type == pkgCache::Dep::Depends) + if (VisitProvides(D) == false) + return false; + return true; +} + /*}}}*/ +// OrderList::DepRemove - Removal ordering /*{{{*/ +// --------------------------------------------------------------------- +/* Removal visits all reverse depends. It considers if the dependency + of the Now state version to see if it is okay with removing this + package. This check should always fail, but is provided for symetery + with the other critical handlers. + + Loops are preprocessed and logged. Removal loops can also be + detected in the critical handler. They are characterized by an + old version of A depending on B but the new version of A conflicting + with B, thus either A or B must break to install. */ +bool pkgOrderList::DepRemove(DepIterator D) +{ + if (D.Reverse() == false) + return true; + for (; D.end() == false; D++) + if (D->Type == pkgCache::Dep::Depends || D->Type == pkgCache::Dep::PreDepends) + { + // Duplication elimination, consider the current version only + if (D.ParentPkg().CurrentVer() != D.ParentVer()) + continue; + + /* We wish to see if the dep on the parent package is okay + in the removed (install) state of the target pkg. */ + if (CheckDep(D) == true) + { + // We want to catch loops with the code below. + if (IsFlag(D.ParentPkg(),AddPending) == false) + continue; + } + + // This is the loop detection + if (IsFlag(D.ParentPkg(),Added) == true || + IsFlag(D.ParentPkg(),AddPending) == true) + { + if (IsFlag(D.ParentPkg(),AddPending) == true) + AddLoop(D); + continue; + } + + if (VisitNode(D.ParentPkg()) == false) + return false; + } + + return true; +} + /*}}}*/ + +// OrderList::AddLoop - Add a loop to the loop list /*{{{*/ +// --------------------------------------------------------------------- +/* We record the loops. This is a relic since loop breaking is done + genericaly as part of the safety routines. */ +bool pkgOrderList::AddLoop(DepIterator D) +{ + if (LoopCount < 0 || LoopCount >= 20) + return false; + + // Skip dups + if (LoopCount != 0) + { + if (Loops[LoopCount - 1].ParentPkg() == D.ParentPkg() || + Loops[LoopCount - 1].TargetPkg() == D.ParentPkg()) + return true; + } + + Loops[LoopCount++] = D; + + // Mark the packages as being part of a loop. + Flag(D.TargetPkg(),Loop); + Flag(D.ParentPkg(),Loop); + return true; +} + /*}}}*/ +// OrderList::WipeFlags - Unset the given flags from all packages /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgOrderList::WipeFlags(unsigned long F) +{ + unsigned long Size = Cache.HeaderP->PackageCount; + for (unsigned long I = 0; I != Size; I++) + Flags[I] &= ~F; +} + /*}}}*/ +// OrderList::CheckDep - Check a dependency for truth /*{{{*/ +// --------------------------------------------------------------------- +/* This performs a complete analysis of the dependency wrt to the + current add list. It returns true if after all events are + performed it is still true. This sort of routine can be approximated + by examining the DepCache, however in convoluted cases of provides + this fails to produce a suitable result. */ +bool pkgOrderList::CheckDep(DepIterator D) +{ + Version **List = D.AllTargets(); + for (Version **I = List; *I != 0; I++) + { + VerIterator Ver(Cache,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + /* The meaning of Added and AddPending is subtle. AddPending is + an indication that the package is looping. Because of the + way ordering works Added means the package will be unpacked + before this one and AddPending means after. It is therefore + correct to ignore AddPending in all cases, but that exposes + reverse-ordering loops which should be ignore. */ + if (IsFlag(Pkg,Added) == true || + (IsFlag(Pkg,AddPending) == true && D.Reverse() == true)) + { + if (Cache[Pkg].InstallVer != *I) + continue; + } + else + if ((Version *)Pkg.CurrentVer() != *I || + Pkg.State() != PkgIterator::NeedsNothing) + continue; + + delete [] List; + + /* Conflicts requires that all versions are not present, depends + just needs one */ + if (D->Type != pkgCache::Dep::Conflicts) + return true; + else + return false; + } + delete [] List; + + /* Conflicts requires that all versions are not present, depends + just needs one */ + if (D->Type == pkgCache::Dep::Conflicts) + return true; + return false; +} + /*}}}*/ diff --git a/apt-pkg/orderlist.h b/apt-pkg/orderlist.h new file mode 100644 index 000000000..0dc8a5038 --- /dev/null +++ b/apt-pkg/orderlist.h @@ -0,0 +1,123 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: orderlist.h,v 1.1 1998/07/07 04:17:01 jgg Exp $ +/* ###################################################################### + + Order List - Represents and Manipulates an ordered list of packages. + + A list of packages can be ordered by a number of conflicting criteria + each given a specific priority. Each package also has a set of flags + indicating some usefull things about it that are derived in the + course of sorting. The pkgPackageManager class uses this class for + all of it's installation ordering needs. + + ##################################################################### */ + /*}}}*/ +// Header section: pkglib +#ifndef PKGLIB_ORDERLIST_H +#define PKGLIB_ORDERLIST_H + +#ifdef __GNUG__ +#pragma interface "pkglib/orderlist.h" +#endif + +#include + +class pkgDepCache; +class pkgOrderList +{ + protected: + + pkgDepCache &Cache; + + // Bring some usefull types into the local scope + typedef pkgCache::PkgIterator PkgIterator; + typedef pkgCache::VerIterator VerIterator; + typedef pkgCache::DepIterator DepIterator; + typedef pkgCache::PrvIterator PrvIterator; + typedef pkgCache::Package Package; + typedef pkgCache::Version Version; + typedef bool (pkgOrderList::*DepFunc)(DepIterator D); + + // These are the currently selected ordering functions + DepFunc Primary; + DepFunc Secondary; + DepFunc RevDepends; + DepFunc Remove; + + // State + Package **End; + Package **List; + DepIterator Loops[20]; + int LoopCount; + int Depth; + unsigned char *Flags; + + // Main visit function + bool VisitNode(PkgIterator Pkg); + bool VisitDeps(DepFunc F,PkgIterator Pkg); + bool VisitRDeps(DepFunc F,PkgIterator Pkg); + bool VisitRProvides(DepFunc F,VerIterator Ver); + bool VisitProvides(DepIterator Pkg); + + // Dependency checking functions. + bool DepUnPackCrit(DepIterator D); + bool DepUnPackPreD(DepIterator D); + bool DepUnPackPre(DepIterator D); + bool DepUnPackDep(DepIterator D); + bool DepConfigure(DepIterator D); + bool DepRemove(DepIterator D); + + // Analysis helpers + bool AddLoop(DepIterator D); + bool CheckDep(DepIterator D); + bool DoRun(); + + // For pre sorting + static pkgOrderList *Me; + static int OrderCompareA(const void *a, const void *b); + static int OrderCompareB(const void *a, const void *b); + int FileCmp(PkgIterator A,PkgIterator B); + + public: + + typedef Package **iterator; + + // State flags + enum Flags {Added = (1 << 0), AddPending = (1 << 1), + Immediate = (1 << 2), Loop = (1 << 3), + UnPacked = (1 << 4), Configured = (1 << 5), + Removed = (1 << 6), + InList = (1 << 7), + States = (UnPacked | Configured | Removed)}; + + // Flag manipulators + inline bool IsFlag(PkgIterator Pkg,unsigned long F) {return (Flags[Pkg->ID] & F) == F;}; + inline bool IsFlag(Package *Pkg,unsigned long F) {return (Flags[Pkg->ID] & F) == F;}; + void Flag(PkgIterator Pkg,unsigned long State, unsigned long F) {Flags[Pkg->ID] = (Flags[Pkg->ID] & (~F)) | State;}; + inline void Flag(PkgIterator Pkg,unsigned long F) {Flags[Pkg->ID] |= F;}; + inline void Flag(Package *Pkg,unsigned long F) {Flags[Pkg->ID] |= F;}; + inline bool IsNow(PkgIterator Pkg) {return (Flags[Pkg->ID] & States) == 0;}; + void WipeFlags(unsigned long F); + + // Accessors + inline iterator begin() {return List;}; + inline iterator end() {return End;}; + inline void push_back(Package *Pkg) {*(End++) = Pkg;}; + inline void push_back(PkgIterator Pkg) {*(End++) = Pkg;}; + inline void pop_back() {End--;}; + inline bool empty() {return End == List;}; + inline unsigned int size() {return End - List;}; + + // Ordering modes + bool OrderCritical(); + bool OrderUnpack(); + bool OrderConfigure(); + + int Score(PkgIterator Pkg); + + pkgOrderList(pkgDepCache &Cache); + ~pkgOrderList(); +}; + +#endif diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc new file mode 100644 index 000000000..ebf67b5ee --- /dev/null +++ b/apt-pkg/packagemanager.cc @@ -0,0 +1,548 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: packagemanager.cc,v 1.1 1998/07/07 04:17:01 jgg Exp $ +/* ###################################################################### + + Package Manager - Abstacts the package manager + + More work is needed in the area of transitioning provides, ie exim + replacing smail. This can cause interesing side effects. + + Other cases involving conflicts+replaces should be tested. + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/packagemanager.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + /*}}}*/ + +// PM::PackageManager - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgPackageManager::pkgPackageManager(pkgDepCache &Cache) : Cache(Cache) +{ + FileNames = new string[Cache.Head().PackageCount]; + List = 0; +} + /*}}}*/ +// PM::PackageManager - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgPackageManager::~pkgPackageManager() +{ + delete List; + delete [] FileNames; +} + /*}}}*/ +// PM::GetArchives - Puts the install archives in the aquire list /*{{{*/ +// --------------------------------------------------------------------- +/* The Source list is used to convert the packages to install into + URNs which are then passed to Aquire to convert to FileNames. */ +bool pkgPackageManager::GetArchives(pkgSourceList &List,pkgAquire &Engine) +{ + pkgControlCache CCache(Cache); + if (_error->PendingError() == true) + return false; + + Engine.OutputDir(PKG_DEB_CA_ARCHIVES); + pkgCache::PkgIterator I = Cache.PkgBegin(); + for (;I.end() != true; I++) + { + // Not interesting + if ((Cache[I].InstallVer == (pkgCache::Version *)I.CurrentVer() && + I.State() != pkgCache::PkgIterator::NeedsUnpack) || + Cache[I].Delete() == true) + continue; + + // Cross match with the source list. + pkgSourceList::const_iterator Dist = List.MatchPkgFile(Cache[I].InstVerIter(Cache)); + if (Dist == List.end()) + { + _error->Warning("Couldn't locate an archive source for package %s",I.Name()); + continue; + } + + // Read in the info record + pkgSPkgCtrlInfo Inf = CCache[Cache[I].InstVerIter(Cache)]; + if (Inf.isNull() == true) + { + _error->Warning("Couldn't locate info for package %s",I.Name()); + continue; + } + + // Isolate the filename + string File = Inf->Find("Filename")->Value(); + if (File.empty() == true) + { + _error->Warning("Couldn't locate an archive for package %s",I.Name()); + continue; + } + + // Generate the get request. + string URI = Dist->ArchiveURI(File); + + // Output file + unsigned int Pos = File.rfind('/'); + if (Pos == File.length()) + return _error->Error("Malformed file line in package %s",I.Name()); + + // Null pos isnt used in present package files + if (Pos == string::npos) + Pos = 0; + else + Pos++; + + if (Engine.Get(URI,string(File,Pos), + Dist->ArchiveInfo(Cache[I].InstVerIter(Cache)), + Cache[I].InstVerIter(Cache)->Size, + Inf->Find("MD5sum")->Value(), + &FileNames[I->ID]) == false) + return false; + } + + return true; +} + /*}}}*/ +// PM::FixMissing - Keep all missing packages /*{{{*/ +// --------------------------------------------------------------------- +/* This is called to correct the installation when packages could not + be downloaded. */ +bool pkgPackageManager::FixMissing() +{ + unsigned char *Touch = new unsigned char[Cache.Head().PackageCount]; + for (PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + // Create the status list that ResolveConflicts needs + if ((Cache[I].DepState & pkgDepCache::DepNowMin) == pkgDepCache::DepNowMin) + Touch[I->ID] = (1 << 0) | (1 << 1); + else + Touch[I->ID] = 1 << 1; + + if (Cache[I].Keep() == true) + continue; + if (FileNames[I->ID].empty() == false || Cache[I].Delete() == true) + continue; + Cache.MarkKeep(I); + } + + // Now downgrade everything that is broken + Cache.ResolveConflicts(Touch); + delete [] Touch; + + return Cache.BrokenCount() == 0; +} + /*}}}*/ + +// PM::DepAlwaysTrue - Returns true if this dep is irrelevent /*{{{*/ +// --------------------------------------------------------------------- +/* The restriction on provides is to eliminate the case when provides + are transitioning between valid states [ie exim to smail] */ +bool pkgPackageManager::DepAlwaysTrue(DepIterator D) +{ + if (D.TargetPkg()->ProvidesList != 0) + return false; + + if ((Cache[D] & pkgDepCache::DepInstall) != 0 && + (Cache[D] & pkgDepCache::DepNow) != 0) + return true; + return false; +} + /*}}}*/ +// PM::CheckRConflicts - Look for reverse conflicts /*{{{*/ +// --------------------------------------------------------------------- +/* This looks over the reverses for a conflicts line that needs early + removal. */ +bool pkgPackageManager::CheckRConflicts(PkgIterator Pkg,DepIterator D, + const char *Ver) +{ + for (;D.end() == false; D++) + { + if (D->Type != pkgDEP_Conflicts) + continue; + + if (D.ParentPkg() == Pkg) + continue; + + if (pkgCheckDep(D.TargetVer(),Ver,D->CompareOp) == false) + continue; + + if (List->IsNow(Pkg) == false) + continue; + + if (EarlyRemove(D.ParentPkg()) == false) + return false; + } + return true; +} + /*}}}*/ +// PM::ConfigureAll - Run the all out configuration /*{{{*/ +// --------------------------------------------------------------------- +/* This configures every package. It is assumed they are all unpacked and + that the final configuration is valid. */ +bool pkgPackageManager::ConfigureAll() +{ + pkgOrderList OList(Cache); + + // Populate the order list + for (pkgOrderList::iterator I = List->begin(); I != List->end(); I++) + if (List->IsFlag(pkgCache::PkgIterator(Cache,*I), + pkgOrderList::UnPacked) == true) + OList.push_back(*I); + + if (OList.OrderConfigure() == false) + return false; + + // Perform the configuring + for (pkgOrderList::iterator I = OList.begin(); I != OList.end(); I++) + { + PkgIterator Pkg(Cache,*I); + + if (Configure(Pkg) == false) + return false; + + List->Flag(Pkg,pkgOrderList::Configured,pkgOrderList::States); + } + + return true; +} + /*}}}*/ +// PM::SmartConfigure - Perform immediate configuration of the pkg /*{{{*/ +// --------------------------------------------------------------------- +/* This routine scheduals the configuration of the given package and all + of it's dependents. */ +bool pkgPackageManager::SmartConfigure(PkgIterator Pkg) +{ + pkgOrderList OList(Cache); + + if (DepAdd(OList,Pkg) == false) + return false; + + if (OList.OrderConfigure() == false) + return false; + + // Perform the configuring + for (pkgOrderList::iterator I = OList.begin(); I != OList.end(); I++) + { + PkgIterator Pkg(Cache,*I); + + if (Configure(Pkg) == false) + return false; + + List->Flag(Pkg,pkgOrderList::Configured,pkgOrderList::States); + } + + // Sanity Check + if (List->IsFlag(Pkg,pkgOrderList::Configured) == false) + return _error->Error("Internal error, could not immediate configure %s",Pkg.Name()); + + return true; +} + /*}}}*/ +// PM::DepAdd - Add all dependents to the oder list /*{{{*/ +// --------------------------------------------------------------------- +/* This recursively adds all dependents to the order list */ +bool pkgPackageManager::DepAdd(pkgOrderList &OList,PkgIterator Pkg,int Depth) +{ + if (OList.IsFlag(Pkg,pkgOrderList::Added) == true) + return true; + if (List->IsFlag(Pkg,pkgOrderList::Configured) == true) + return true; + if (List->IsFlag(Pkg,pkgOrderList::UnPacked) == false) + return false; + + + // Put the package on the list + OList.push_back(Pkg); + OList.Flag(Pkg,pkgOrderList::Added); + Depth++; + + // Check the dependencies to see if they are all satisfied. + bool Bad = false; + for (DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); D.end() == false;) + { + if (D->Type != pkgDEP_Depends && D->Type != pkgDEP_PreDepends) + { + D++; + continue; + } + + // Grok or groups + Bad = true; + for (bool LastOR = true; D.end() == false && LastOR == true; D++) + { + LastOR = (D->CompareOp & pkgOP_OR) == pkgOP_OR; + + if (Bad == false) + continue; + + Version **VList = D.AllTargets(); + for (Version **I = VList; *I != 0 && Bad == true; I++) + { + VerIterator Ver(Cache,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + // See if the current version is ok + if (Pkg.CurrentVer() == Ver && List->IsNow(Pkg) == true && + Pkg.State() == PkgIterator::NeedsNothing) + { + Bad = false; + continue; + } + + // Not the install version + if (Cache[Pkg].InstallVer != *I || + (Cache[Pkg].Keep() == true && Pkg.State() == PkgIterator::NeedsNothing)) + continue; + if (List->IsFlag(Pkg,pkgOrderList::UnPacked) == true) + Bad = !DepAdd(OList,Pkg,Depth); + if (List->IsFlag(Pkg,pkgOrderList::Configured) == true) + Bad = false; + } + delete [] VList; + } + + if (Bad == true) + { + OList.Flag(Pkg,0,pkgOrderList::Added); + OList.pop_back(); + Depth--; + return false; + } + } + + Depth--; + return true; +} + /*}}}*/ +// PM::EarlyRemove - Perform removal of packages before their time /*{{{*/ +// --------------------------------------------------------------------- +/* This is called to deal with conflicts arising from unpacking */ +bool pkgPackageManager::EarlyRemove(PkgIterator Pkg) +{ + if (List->IsNow(Pkg) == false) + return true; + + // Already removed it + if (List->IsFlag(Pkg,pkgOrderList::Removed) == true) + return true; + + // Woops, it will not be re-installed! + if (List->IsFlag(Pkg,pkgOrderList::InList) == false) + return false; + + bool Res = SmartRemove(Pkg); + if (Cache[Pkg].Delete() == false) + List->Flag(Pkg,pkgOrderList::Removed,pkgOrderList::States); + + return Res; +} + /*}}}*/ +// PM::SmartRemove - Removal Helper /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgPackageManager::SmartRemove(PkgIterator Pkg) +{ + if (List->IsNow(Pkg) == false) + return true; + + List->Flag(Pkg,pkgOrderList::Configured,pkgOrderList::States); + return Remove(Pkg); +} + /*}}}*/ +// PM::SmartUnPack - Install helper /*{{{*/ +// --------------------------------------------------------------------- +/* This performs the task of handling pre-depends. */ +bool pkgPackageManager::SmartUnPack(PkgIterator Pkg) +{ + // Check if it is already unpacked + if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure && + Cache[Pkg].Keep() == true) + { + List->Flag(Pkg,pkgOrderList::UnPacked,pkgOrderList::States); + if (List->IsFlag(Pkg,pkgOrderList::Immediate) == true) + if (SmartConfigure(Pkg) == false) + return _error->Error("Internal Error, Could not perform immediate configuraton"); + return true; + } + + /* See if this packages install version has any predependencies + that are not met by 'now' packages. */ + for (DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); + D.end() == false; D++) + { + if (D->Type == pkgDEP_PreDepends) + { + // Look for possible ok targets. + Version **VList = D.AllTargets(); + bool Bad = true; + for (Version **I = VList; *I != 0 && Bad == true; I++) + { + VerIterator Ver(Cache,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + // See if the current version is ok + if (Pkg.CurrentVer() == Ver && List->IsNow(Pkg) == true && + Pkg.State() == PkgIterator::NeedsNothing) + { + Bad = false; + continue; + } + } + + // Look for something that could be configured. + for (Version **I = VList; *I != 0 && Bad == true; I++) + { + VerIterator Ver(Cache,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + // Not the install version + if (Cache[Pkg].InstallVer != *I || + (Cache[Pkg].Keep() == true && Pkg.State() == PkgIterator::NeedsNothing)) + continue; + + Bad = !SmartConfigure(Pkg); + } + + delete [] VList; + + if (Bad == true) + return _error->Error("Internal Error, Couldn't configure a pre-depend"); + + continue; + } + + if (D->Type == pkgDEP_Conflicts) + { + /* Look for conflicts. Two packages that are both in the install + state cannot conflict so we don't check.. */ + Version **VList = D.AllTargets(); + for (Version **I = VList; *I != 0; I++) + { + VerIterator Ver(Cache,*I); + PkgIterator Pkg = Ver.ParentPkg(); + + // See if the current version is conflicting + if (Pkg.CurrentVer() == Ver && List->IsNow(Pkg) == true) + { + if (EarlyRemove(Pkg) == false) + return _error->Error("Internal Error, Could not early remove %s",Pkg.Name()); + } + } + delete [] VList; + } + } + + // Check for reverse conflicts. + CheckRConflicts(Pkg,Pkg.RevDependsList(), + Cache[Pkg].InstVerIter(Cache).VerStr()); + for (PrvIterator P = Cache[Pkg].InstVerIter(Cache).ProvidesList(); + P.end() == false; P++) + CheckRConflicts(Pkg,P.ParentPkg().RevDependsList(),P.ProvideVersion()); + + if (Install(Pkg,FileNames[Pkg->ID]) == false) + return false; + + List->Flag(Pkg,pkgOrderList::UnPacked,pkgOrderList::States); + + // Perform immedate configuration of the package. + if (List->IsFlag(Pkg,pkgOrderList::Immediate) == true) + if (SmartConfigure(Pkg) == false) + return _error->Error("Internal Error, Could not perform immediate configuraton"); + + return true; +} + /*}}}*/ +// PM::OrderInstall - Installation ordering routine /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgPackageManager::OrderInstall() +{ + delete List; + List = new pkgOrderList(Cache); + + // Generate the list of affected packages and sort it + for (PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) + { + // Consider all depends + if ((I->Flags & pkgFLAG_Essential) == pkgFLAG_Essential) + { + List->Flag(I,pkgOrderList::Immediate); + if (Cache[I].InstallVer != 0) + for (DepIterator D = Cache[I].InstVerIter(Cache).DependsList(); + D.end() == false; D++) + if (D->Type == pkgDEP_Depends || D->Type == pkgDEP_PreDepends) + List->Flag(D.TargetPkg(),pkgOrderList::Immediate); + if (I->CurrentVer != 0) + for (DepIterator D = I.CurrentVer().DependsList(); + D.end() == false; D++) + if (D->Type == pkgDEP_Depends || D->Type == pkgDEP_PreDepends) + List->Flag(D.TargetPkg(),pkgOrderList::Immediate); + } + + // Not interesting + if ((Cache[I].Keep() == true || + Cache[I].InstVerIter(Cache) == I.CurrentVer()) && + I.State() == pkgCache::PkgIterator::NeedsNothing) + continue; + + // Append it to the list + List->push_back(I); + + if ((I->Flags & pkgFLAG_ImmediateConf) == pkgFLAG_ImmediateConf) + List->Flag(I,pkgOrderList::Immediate); + } + + if (List->OrderUnpack() == false) + return _error->Error("Internal ordering error"); + + for (pkgOrderList::iterator I = List->begin(); I != List->end(); I++) + { + PkgIterator Pkg(Cache,*I); + + // Sanity check + if (Cache[Pkg].Keep() == true && Pkg.State() == pkgCache::PkgIterator::NeedsNothing) + return _error->Error("Internal Error, trying to manipulate a kept package"); + + // Perform a delete or an install + if (Cache[Pkg].Delete() == true) + { + if (SmartRemove(Pkg) == false) + return false; + } + else + if (SmartUnPack(Pkg) == false) + return false; + } + + // Final run through the configure phase + if (ConfigureAll() == false) + return false; + + // Sanity check + for (pkgOrderList::iterator I = List->begin(); I != List->end(); I++) + if (List->IsFlag(*I,pkgOrderList::Configured) == false) + return _error->Error("Internal error, packages left unconfigured. %s", + PkgIterator(Cache,*I).Name()); + + return true; +} + /*}}}*/ +// PM::DoInstall - Does the installation /*{{{*/ +// --------------------------------------------------------------------- +/* This uses the filenames in FileNames and the information in the + DepCache to perform the installation of packages.*/ +bool pkgPackageManager::DoInstall() +{ + return OrderInstall() && Go(); +} + /*}}}*/ diff --git a/apt-pkg/packagemanager.h b/apt-pkg/packagemanager.h new file mode 100644 index 000000000..6020cbefd --- /dev/null +++ b/apt-pkg/packagemanager.h @@ -0,0 +1,84 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: packagemanager.h,v 1.1 1998/07/07 04:17:01 jgg Exp $ +/* ###################################################################### + + Package Manager - Abstacts the package manager + + Three steps are + - Aquiration of archives (stores the list of final file names) + - Sorting of operations + - Inokation of package manager + + This is the final stage when the package cache entities get converted + into file names and the state stored in a DepCache is transformed + into a series of operations. + + In the final scheme of things this may serve as a director class to + access the actual install methods based on the file type being + installed. + + ##################################################################### */ + /*}}}*/ +// Header section: pkglib +#ifndef PKGLIB_PACKAGEMANAGER_H +#define PKGLIB_PACKAGEMANAGER_H + +#ifdef __GNUG__ +#pragma interface "pkglib/packagemanager.h" +#endif + +#include +#include + +class pkgAquire; +class pkgDepCache; +class pkgSourceList; +class pkgOrderList; +class pkgPackageManager +{ + protected: + string *FileNames; + pkgDepCache &Cache; + pkgOrderList *List; + + // Bring some usefull types into the local scope + typedef pkgCache::PkgIterator PkgIterator; + typedef pkgCache::VerIterator VerIterator; + typedef pkgCache::DepIterator DepIterator; + typedef pkgCache::PrvIterator PrvIterator; + typedef pkgCache::Version Version; + typedef pkgCache::Package Package; + + bool DepAdd(pkgOrderList &Order,PkgIterator P,int Depth = 0); + bool OrderInstall(); + bool CheckRConflicts(PkgIterator Pkg,DepIterator Dep,const char *Ver); + + // Analysis helpers + bool DepAlwaysTrue(DepIterator D); + + // Install helpers + bool ConfigureAll(); + bool SmartConfigure(PkgIterator Pkg); + bool SmartUnPack(PkgIterator Pkg); + bool SmartRemove(PkgIterator Pkg); + bool EarlyRemove(PkgIterator Pkg); + + // The Actuall installation implementation + virtual bool Install(PkgIterator /*Pkg*/,string /*File*/) {return false;}; + virtual bool Configure(PkgIterator /*Pkg*/) {return false;}; + virtual bool Remove(PkgIterator /*Pkg*/) {return false;}; + virtual bool Go() {return false;}; + + public: + + // The three stages + bool GetArchives(pkgSourceList &List,pkgAquire &Engine); + bool DoInstall(); + bool FixMissing(); + + pkgPackageManager(pkgDepCache &Cache); + virtual ~pkgPackageManager(); +}; + +#endif diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc index b9dbf34ec..085225031 100644 --- a/apt-pkg/pkgcache.cc +++ b/apt-pkg/pkgcache.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: pkgcache.cc,v 1.4 1998/07/05 05:33:53 jgg Exp $ +// $Id: pkgcache.cc,v 1.5 1998/07/07 04:17:02 jgg Exp $ /* ###################################################################### Package Cache - Accessor code for the cache @@ -20,6 +20,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/pkgcache.h" +#pragma implementation "pkglib/cacheiterators.h" +#endif #include #include #include @@ -199,13 +203,13 @@ void pkgCache::PkgIterator::operator ++(int) /* By this we mean if it is either cleanly installed or cleanly removed. */ pkgCache::PkgIterator::OkState pkgCache::PkgIterator::State() const { - if (Pkg->CurrentState == UnPacked || - Pkg->CurrentState == HalfConfigured) + if (Pkg->CurrentState == State::UnPacked || + Pkg->CurrentState == State::HalfConfigured) return NeedsConfigure; - if (Pkg->CurrentState == UnInstalled || - Pkg->CurrentState == HalfInstalled || - Pkg->InstState != Ok) + if (Pkg->CurrentState == State::UnInstalled || + Pkg->CurrentState == State::HalfInstalled || + Pkg->InstState != State::Ok) return NeedsUnpack; return NeedsNothing; @@ -217,8 +221,8 @@ pkgCache::PkgIterator::OkState pkgCache::PkgIterator::State() const conflicts. */ bool pkgCache::DepIterator::IsCritical() { - if (Dep->Type == Conflicts || Dep->Type == Depends || - Dep->Type == PreDepends) + if (Dep->Type == Dep::Conflicts || Dep->Type == Dep::Depends || + Dep->Type == Dep::PreDepends) return true; return false; } @@ -293,7 +297,7 @@ pkgCache::Version **pkgCache::DepIterator::AllTargets() if (pkgCheckDep(TargetVer(),I.VerStr(),Dep->CompareOp) == false) continue; - if (Dep->Type == Conflicts && ParentPkg() == I.ParentPkg()) + if (Dep->Type == Dep::Conflicts && ParentPkg() == I.ParentPkg()) continue; Size++; @@ -307,7 +311,7 @@ pkgCache::Version **pkgCache::DepIterator::AllTargets() if (pkgCheckDep(TargetVer(),I.ProvideVersion(),Dep->CompareOp) == false) continue; - if (Dep->Type == Conflicts && ParentPkg() == I.OwnerPkg()) + if (Dep->Type == Dep::Conflicts && ParentPkg() == I.OwnerPkg()) continue; Size++; diff --git a/apt-pkg/pkgcache.h b/apt-pkg/pkgcache.h index 836418c2c..852949b1f 100644 --- a/apt-pkg/pkgcache.h +++ b/apt-pkg/pkgcache.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: pkgcache.h,v 1.4 1998/07/05 05:33:55 jgg Exp $ +// $Id: pkgcache.h,v 1.5 1998/07/07 04:17:03 jgg Exp $ /* ###################################################################### Cache - Structure definitions for the cache file @@ -20,6 +20,10 @@ #ifndef PKGLIB_PKGCACHE_H #define PKGLIB_PKGCACHE_H +#ifdef __GNUG__ +#pragma interface "pkglib/pkgcache.h" +#endif + #include #include #include @@ -52,19 +56,29 @@ class pkgCache friend VerFileIterator; // These are all the constants used in the cache structures - enum DepType {Depends=1,PreDepends=2,Suggests=3,Recommends=4, - Conflicts=5,Replaces=6}; - enum VerPriority {Important=1,Required=2,Standard=3,Optional=5,Extra=5}; - enum PkgSelectedState {Unknown=0,Install=1,Hold=2,DeInstall=3,Purge=4}; - enum PkgFlags {Auto=(1<<0),New=(1<<1),Obsolete=(1<<2),Essential=(1<<3), - ImmediateConf=(1<<4)}; - enum PkgInstState {Ok=0,ReInstReq=1,HoldInst=2,HoldReInstReq=3}; - enum PkgCurrentState {NotInstalled=0,UnPacked=1,HalfConfigured=2, - UnInstalled=3,HalfInstalled=4,ConfigFiles=5, - Installed=6}; - enum PkgFFlags {NotSource=(1<<0)}; - enum DepCompareOp {Or=0x10,NoOp=0,LessEq=0x1,GreaterEq=0x2,Less=0x3, - Greater=0x4,Equals=0x5,NotEquals=0x6}; + struct Dep + { + enum DepType {Depends=1,PreDepends=2,Suggests=3,Recommends=4, + Conflicts=5,Replaces=6}; + enum DepCompareOp {Or=0x10,NoOp=0,LessEq=0x1,GreaterEq=0x2,Less=0x3, + Greater=0x4,Equals=0x5,NotEquals=0x6}; + }; + + struct State + { + enum VerPriority {Important=1,Required=2,Standard=3,Optional=5,Extra=5}; + enum PkgSelectedState {Unknown=0,Install=1,Hold=2,DeInstall=3,Purge=4}; + enum PkgInstState {Ok=0,ReInstReq=1,HoldInst=2,HoldReInstReq=3}; + enum PkgCurrentState {NotInstalled=0,UnPacked=1,HalfConfigured=2, + UnInstalled=3,HalfInstalled=4,ConfigFiles=5,Installed=6}; + }; + + struct Flag + { + enum PkgFlags {Auto=(1<<0),New=(1<<1),Obsolete=(1<<2),Essential=(1<<3), + ImmediateConf=(1<<4)}; + enum PkgFFlags {NotSource=(1<<0)}; + }; protected: diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc index acc05f133..aac3f77d9 100644 --- a/apt-pkg/pkgcachegen.cc +++ b/apt-pkg/pkgcachegen.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: pkgcachegen.cc,v 1.6 1998/07/05 05:53:52 jgg Exp $ +// $Id: pkgcachegen.cc,v 1.7 1998/07/07 04:17:04 jgg Exp $ /* ###################################################################### Package Cache Generator - Generator for the cache structure. @@ -10,6 +10,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/pkgcachegen.h" +#endif + #include #include #include diff --git a/apt-pkg/pkgcachegen.h b/apt-pkg/pkgcachegen.h index 395afdc83..c883828eb 100644 --- a/apt-pkg/pkgcachegen.h +++ b/apt-pkg/pkgcachegen.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: pkgcachegen.h,v 1.3 1998/07/05 05:33:57 jgg Exp $ +// $Id: pkgcachegen.h,v 1.4 1998/07/07 04:17:05 jgg Exp $ /* ###################################################################### Package Cache Generator - Generator for the cache structure. @@ -13,6 +13,10 @@ #ifndef PKGLIB_PKGCACHEGEN_H #define PKGLIB_PKGCACHEGEN_H +#ifdef __GNUG__ +#pragma interface "pkglib/pkgcachegen.h" +#endif + #include class pkgCacheGenerator diff --git a/apt-pkg/sourcelist.cc b/apt-pkg/sourcelist.cc new file mode 100644 index 000000000..62d5e6fcd --- /dev/null +++ b/apt-pkg/sourcelist.cc @@ -0,0 +1,465 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: sourcelist.cc,v 1.1 1998/07/07 04:17:06 jgg Exp $ +/* ###################################################################### + + List of Sources + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/sourcelist.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + /*}}}*/ + +// SourceList::pkgSourceList - Constructors /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgSourceList::pkgSourceList() +{ +} + +pkgSourceList::pkgSourceList(string File) +{ + Read(File); +} + /*}}}*/ +// SourceList::ReadMainList - Read the main source list from etc /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSourceList::ReadMainList() +{ + return Read(PKG_DEB_CF_SOURCELIST); +} + /*}}}*/ +// SourceList::Read - Parse the sourcelist file /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSourceList::Read(string File) +{ + // Open the stream for reading + ifstream F(File.c_str(),ios::in | ios::nocreate); + if (!F != 0) + return _error->Errno("ifstream::ifstream","Opening %s",File.c_str()); + + List.erase(List.begin(),List.end()); + char Buffer[300]; + + int CurLine = 0; + while (F.eof() == false) + { + F.getline(Buffer,sizeof(Buffer)); + CurLine++; + _strtabexpand(Buffer,sizeof(Buffer)); + _strstrip(Buffer); + + // Comment or blank + if (Buffer[0] == '#' || Buffer[0] == 0) + continue; + + // Grok it + string Type; + string URI; + Item Itm; + char *C = Buffer; + if (ParseQuoteWord(C,Type) == false) + return _error->Error("Malformed line %u in source list %s (type)",CurLine,File.c_str()); + if (ParseQuoteWord(C,URI) == false) + return _error->Error("Malformed line %u in source list %s (URI)",CurLine,File.c_str()); + if (ParseQuoteWord(C,Itm.Dist) == false) + return _error->Error("Malformed line %u in source list %s (dist)",CurLine,File.c_str()); + if (Itm.SetType(Type) == false) + return _error->Error("Malformed line %u in source list %s (type parse)",CurLine,File.c_str()); + if (Itm.SetURI(URI) == false) + return _error->Error("Malformed line %u in source list %s (URI parse)",CurLine,File.c_str()); + + // Check for an absolute dists specification. + if (Itm.Dist.empty() == false && Itm.Dist[Itm.Dist.size() - 1] == '/') + { + if (ParseQuoteWord(C,Itm.Section) == true) + return _error->Error("Malformed line %u in source list %s (Absolute dist)",CurLine,File.c_str()); + Itm.Dist = SubstVar(Itm.Dist,"$(ARCH)",PKG_DEB_ARCH); + List.push_back(Itm); + continue; + } + + // Grab the rest of the dists + if (ParseQuoteWord(C,Itm.Section) == false) + return _error->Error("Malformed line %u in source list %s (dist parse)",CurLine,File.c_str()); + + do + { + List.push_back(Itm); + } + while (ParseQuoteWord(C,Itm.Section) == true); + } + return true; +} + /*}}}*/ +// SourceList::SanitizeURI - Hash the uri /*{{{*/ +// --------------------------------------------------------------------- +/* This converts a URI into a safe filename. It quotes all unsafe characters + and converts / to _ and removes the scheme identifier. */ +string pkgSourceList::SanitizeURI(string URI) +{ + string::const_iterator I = URI.begin() + URI.find(':') + 1; + for (; I < URI.end() && *I == '/'; I++); + + // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF"; + URI = QuoteString(string(I,URI.end() - I),"\\|{}[]<>\"^~_=!@#$%^&*"); + string::iterator J = URI.begin(); + for (; J != URI.end(); J++) + if (*J == '/') + *J = '_'; + return URI; +} + /*}}}*/ +// SourceList::MatchPkgFile - Find the package file that has the ver /*{{{*/ +// --------------------------------------------------------------------- +/* This will return List.end() if it could not find the matching + file */ +pkgSourceList::const_iterator pkgSourceList::MatchPkgFile(pkgCache::VerIterator Ver) +{ + string Base = PKG_DEB_ST_LIST; + for (const_iterator I = List.begin(); I != List.end(); I++) + { + string URI = I->PackagesURI(); + switch (I->Type) + { + case Item::Deb: + if (Base + SanitizeURI(URI) == Ver.File().FileName()) + return I; + break; + }; + } + return List.end(); +} + /*}}}*/ +// SourceList::Item << - Writes the item to a stream /*{{{*/ +// --------------------------------------------------------------------- +/* This is not suitable for rebuilding the sourcelist file but it good for + debugging. */ +ostream &operator <<(ostream &O,pkgSourceList::Item &Itm) +{ + O << Itm.Type << ' ' << Itm.URI << ' ' << Itm.Dist << ' ' << Itm.Section; + return O; +} + /*}}}*/ +// SourceList::Item::SetType - Sets the distribution type /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgSourceList::Item::SetType(string S) +{ + if (S == "deb") + { + Type = Deb; + return true; + } + + return true; +} + /*}}}*/ +// SourceList::Item::SetURI - Set the URI /*{{{*/ +// --------------------------------------------------------------------- +/* For simplicity we strip the scheme off the uri */ +bool pkgSourceList::Item::SetURI(string S) +{ + if (S.empty() == true) + return false; + + if (S.find(':') == string::npos) + return false; + + S = SubstVar(S,"$(ARCH)",PKG_DEB_ARCH); + + // Make sure that the URN is / postfixed + URI = S; + if (URI[URI.size() - 1] != '/') + URI += '/'; + + return true; +} + /*}}}*/ +// SourceList::Item::PackagesURI - Returns a URI to the packages file /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string pkgSourceList::Item::PackagesURI() const +{ + string Res; + switch (Type) + { + case Deb: + if (Dist[Dist.size() - 1] == '/') + Res = URI + Dist; + else + Res = URI + "dists/" + Dist + '/' + Section + + "/binary-" + PKG_DEB_ARCH + '/'; + + Res += "Packages"; + break; + }; + return Res; +} + /*}}}*/ +// SourceList::Item::PackagesInfo - Shorter version of the URI /*{{{*/ +// --------------------------------------------------------------------- +/* This is a shorter version that is designed to be < 60 chars or so */ +string pkgSourceList::Item::PackagesInfo() const +{ + string Res; + switch (Type) + { + case Deb: + Res += SiteOnly(URI) + ' '; + if (Dist[Dist.size() - 1] == '/') + Res += Dist; + else + Res += Dist + '/' + Section; + + Res += " Packages"; + break; + }; + return Res; +} + /*}}}*/ +// SourceList::Item::ArchiveInfo - Shorter version of the archive spec /*{{{*/ +// --------------------------------------------------------------------- +/* This is a shorter version that is designed to be < 60 chars or so */ +string pkgSourceList::Item::ArchiveInfo(pkgCache::VerIterator Ver) const +{ + string Res; + switch (Type) + { + case Deb: + Res += SiteOnly(URI) + ' '; + if (Dist[Dist.size() - 1] == '/') + Res += Dist; + else + Res += Dist + '/' + Section; + + Res += " "; + Res += Ver.ParentPkg().Name(); + break; + }; + return Res; +} + /*}}}*/ +// SourceList::Item::ArchiveURI - Returns a URI to the given archive /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string pkgSourceList::Item::ArchiveURI(string File) const +{ + string Res; + switch (Type) + { + case Deb: + Res = URI + File; + break; + }; + return Res; +} + /*}}}*/ +// SourceList::Item::SiteOnly - Strip off the path part of a URI /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string pkgSourceList::Item::SiteOnly(string URI) const +{ + unsigned int Pos = URI.find(':'); + if (Pos == string::npos || Pos + 3 > URI.length()) + return URI; + if (URI[Pos + 1] != '/' || URI[Pos + 2] != '/') + return URI; + + Pos = URI.find('/',Pos + 3); + if (Pos == string::npos) + return URI; + return string(URI,0,Pos); +} + /*}}}*/ + +// UpdateMeta - Update the meta information /*{{{*/ +// --------------------------------------------------------------------- +/* The meta information is package files, revision information and mirror + lists. */ +bool pkgUpdateMeta(pkgSourceList &List,pkgAquire &Engine) +{ + if (Engine.OutputDir(PKG_DEB_ST_LIST) == false) + return false; + + for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++) + { + string URI = I->PackagesURI(); + string GetInfo = I->PackagesInfo(); + switch (I->Type) + { + case pkgSourceList::Item::Deb: + if (Engine.Get(URI + ".gz",List.SanitizeURI(URI),GetInfo) == false) + return false; + break; + }; + } + + return true; +} + /*}}}*/ +// MakeSrcCache - Generate a cache file of all the package files /*{{{*/ +// --------------------------------------------------------------------- +/* This goes over the source list and builds a cache of all the package + files. */ +bool pkgMakeSrcCache(pkgSourceList &List) +{ + // First we date check the cache + bool Bad = false; + while (Bad == false) + { + if (FileExists(PKG_DEB_CA_SRCCACHE) == false) + break; + + pkgCache Cache(PKG_DEB_CA_SRCCACHE,true,true); + if (_error->PendingError() == true) + { + _error->Discard(); + break; + } + + // They are certianly out of sync + if (Cache.Head().PackageFileCount != List.size()) + break; + + for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++) + { + // Search for a match in the source list + Bad = true; + for (pkgSourceList::const_iterator I = List.begin(); + I != List.end(); I++) + { + string File = string(PKG_DEB_ST_LIST) + + List.SanitizeURI(I->PackagesURI()); + if (F.FileName() == File) + { + Bad = false; + break; + } + } + + // Check if the file matches what was cached + Bad |= !F.IsOk(); + if (Bad == true) + break; + } + + if (Bad == false) + return true; + } + + unlink(PKG_DEB_CA_SRCCACHE); + pkgCache::MergeState Merge(PKG_DEB_CA_SRCCACHE); + if (_error->PendingError() == true) + return false; + + for (pkgSourceList::const_iterator I = List.begin(); I != List.end(); I++) + { + string File = string(PKG_DEB_ST_LIST) + List.SanitizeURI(I->PackagesURI()); + if (Merge.MergePackageFile(File,"??","??") == false) + return false; + } + + return true; +} + /*}}}*/ +// MakeStatusCache - Generates a cache that includes the status files /*{{{*/ +// --------------------------------------------------------------------- +/* This copies the package source cache and then merges the status and + xstatus files into it. */ +bool pkgMakeStatusCache() +{ + // Quickly check if the existing package cache is ok + bool Bad = false; + while (Bad == false) + { + if (FileExists(PKG_DEB_CA_PKGCACHE) == false) + break; + + /* We check the dates of the two caches. This takes care of most things + quickly and easially */ + struct stat Src; + struct stat Pkg; + if (stat(PKG_DEB_CA_PKGCACHE,&Pkg) != 0 || + stat(PKG_DEB_CA_SRCCACHE,&Src) != 0) + break; + if (difftime(Src.st_mtime,Pkg.st_mtime) > 0) + break; + + pkgCache Cache(PKG_DEB_CA_PKGCACHE,true,true); + if (_error->PendingError() == true) + { + _error->Discard(); + break; + } + + for (pkgCache::PkgFileIterator F(Cache); F.end() == false; F++) + { + if (F.IsOk() == false) + { + Bad = true; + break; + } + } + + if (Bad == false) + return true; + } + + // Check the integrity of the source cache. + { + pkgCache Cache(PKG_DEB_CA_SRCCACHE,true,true); + if (_error->PendingError() == true) + return false; + } + + // Sub scope so that merge destructs before we rename the file... + string Cache = PKG_DEB_CA_PKGCACHE ".new"; + { + if (CopyFile(PKG_DEB_CA_SRCCACHE,Cache) == false) + return false; + + pkgCache::MergeState Merge(Cache); + if (_error->PendingError() == true) + return false; + + // Merge in the user status file + if (FileExists(PKG_DEB_ST_USERSTATUS) == true) + if (Merge.MergePackageFile(PKG_DEB_ST_USERSTATUS,"status","0", + pkgFLAG_NotSource) == false) + return false; + + // Merge in the extra status file + if (FileExists(PKG_DEB_ST_XSTATUS) == true) + if (Merge.MergePackageFile(PKG_DEB_ST_XSTATUS,"status","0", + pkgFLAG_NotSource) == false) + return false; + + // Merge in the status file + if (Merge.MergePackageFile("/var/lib/dpkg/status","status","0", + pkgFLAG_NotSource) == false) + return false; + } + + if (rename(Cache.c_str(),PKG_DEB_CA_PKGCACHE) != 0) + return false; + + return true; +} + /*}}}*/ diff --git a/apt-pkg/sourcelist.h b/apt-pkg/sourcelist.h new file mode 100644 index 000000000..986d5e9e8 --- /dev/null +++ b/apt-pkg/sourcelist.h @@ -0,0 +1,78 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: sourcelist.h,v 1.1 1998/07/07 04:17:06 jgg Exp $ +/* ###################################################################### + + SourceList - Manage a list of sources + + The Source List class provides access to a list of sources. It + can read them from a file and generate a list of all the permutations. + + ##################################################################### */ + /*}}}*/ +// Header section: pkglib +#ifndef PKGLIB_SOURCELIST_H +#define PKGLIB_SOURCELIST_H + +#include +#include +#include +#include + +#ifdef __GNUG__ +#pragma interface "pkglib/sourcelist.h" +#endif + +class pkgAquire; +class pkgSourceList +{ + public: + + /* Each item in the source list, each line can have more than one + item */ + struct Item + { + enum {Deb} Type; + + string URI; + string Dist; + string Section; + + bool SetType(string S); + bool SetURI(string S); + string PackagesURI() const; + string PackagesInfo() const; + string SiteOnly(string URI) const; + string ArchiveInfo(pkgCache::VerIterator Ver) const; + string ArchiveURI(string File) const; + }; + typedef vector::const_iterator const_iterator; + + protected: + + vector List; + + public: + + bool ReadMainList(); + bool Read(string File); + string SanitizeURI(string URI); + const_iterator MatchPkgFile(pkgCache::VerIterator Ver); + + // List accessors + inline const_iterator begin() const {return List.begin();}; + inline const_iterator end() const {return List.end();}; + inline unsigned int size() const {return List.size();}; + inline bool empty() const {return List.empty();}; + + pkgSourceList(); + pkgSourceList(string File); +}; + +bool pkgUpdateMeta(pkgSourceList &List,pkgAquire &Engine); +bool pkgMakeSrcCache(pkgSourceList &List); +bool pkgMakeStatusCache(); + +ostream &operator <<(ostream &O,pkgSourceList::Item &Itm); + +#endif diff --git a/apt-pkg/tagfile.cc b/apt-pkg/tagfile.cc index 9710b7615..b8845a3b1 100644 --- a/apt-pkg/tagfile.cc +++ b/apt-pkg/tagfile.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: tagfile.cc,v 1.4 1998/07/05 05:33:58 jgg Exp $ +// $Id: tagfile.cc,v 1.5 1998/07/07 04:17:06 jgg Exp $ /* ###################################################################### Fast scanner for RFC-822 type header information @@ -11,6 +11,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/tagfile.h" +#endif + #include #include diff --git a/apt-pkg/tagfile.h b/apt-pkg/tagfile.h index 661ac4f23..c1fffcf13 100644 --- a/apt-pkg/tagfile.h +++ b/apt-pkg/tagfile.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: tagfile.h,v 1.2 1998/07/05 05:33:59 jgg Exp $ +// $Id: tagfile.h,v 1.3 1998/07/07 04:17:07 jgg Exp $ /* ###################################################################### Fast scanner for RFC-822 type header information @@ -21,6 +21,10 @@ #ifndef PKGLIB_TAGFILE_H #define PKGLIB_TAGFILE_H +#ifdef __GNUG__ +#pragma interface "pkglib/tagfile.h" +#endif + #include class pkgTagSection diff --git a/apt-pkg/version.cc b/apt-pkg/version.cc index 174622c91..7eb85726c 100644 --- a/apt-pkg/version.cc +++ b/apt-pkg/version.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: version.cc,v 1.2 1998/07/04 05:57:40 jgg Exp $ +// $Id: version.cc,v 1.3 1998/07/07 04:17:08 jgg Exp $ /* ###################################################################### Version - Version string @@ -17,6 +17,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#ifdef __GNUG__ +#pragma implementation "pkglib/version.h" +#endif + #include #include @@ -212,32 +216,32 @@ bool pkgCheckDep(const char *DepVer,const char *PkgVer,int Op) int Res = pkgVersionCompare(PkgVer,DepVer); switch (Op & 0x0F) { - case pkgCache::LessEq: + case pkgCache::Dep::LessEq: if (Res <= 0) return true; break; - case pkgCache::GreaterEq: + case pkgCache::Dep::GreaterEq: if (Res >= 0) return true; break; - case pkgCache::Less: + case pkgCache::Dep::Less: if (Res < 0) return true; break; - case pkgCache::Greater: + case pkgCache::Dep::Greater: if (Res > 0) return true; break; - case pkgCache::Equals: + case pkgCache::Dep::Equals: if (Res == 0) return true; break; - case pkgCache::NotEquals: + case pkgCache::Dep::NotEquals: if (Res != 0) return true; break; diff --git a/apt-pkg/version.h b/apt-pkg/version.h index a30246946..3ced5d3e4 100644 --- a/apt-pkg/version.h +++ b/apt-pkg/version.h @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: version.h,v 1.1 1998/07/02 02:58:13 jgg Exp $ +// $Id: version.h,v 1.2 1998/07/07 04:17:09 jgg Exp $ /* ###################################################################### Version - Version string @@ -15,6 +15,10 @@ #ifndef PKGLIB_VERSION_H #define PKGLIB_VERSION_H +#ifdef __GNUG__ +#pragma interface "pkglib/version.h" +#endif + #include class pkgVersion -- cgit v1.2.3