
30 changed files with 4841 additions and 115 deletions
@ -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 <pkglib/algorithms.h> |
|||
#include <pkglib/error.h> |
|||
#include <pkglib/pkgelement.h> |
|||
#include <iostream.h> |
|||
/*}}}*/ |
|||
|
|||
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; |
|||
} |
|||
/*}}}*/ |
@ -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 <pkglib/packagemanager.h> |
|||
#include <pkglib/depcache.h> |
|||
|
|||
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 |
@ -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 <pkglib/configuration.h> |
|||
#include <strutl.h> |
|||
|
|||
#include <stdio.h> |
|||
/*}}}*/ |
|||
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; |
|||
} |
|||
/*}}}*/ |
@ -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 <string> |
|||
|
|||
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 |
@ -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 <jgg@gpu.srv.ualberta.ca> |
|||
|
|||
##################################################################### */ |
|||
/*}}}*/ |
|||
// Includes /*{{{*/
|
|||
#include <strutl.h> |
|||
#include <ctype.h> |
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
/*}}}*/ |
|||
|
|||
// 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++; |
|||
} |
|||