Index: apt-pkg/depcache.cc =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/depcache.cc,v retrieving revision 1.1 diff -u -b -r1.1 depcache.cc --- apt-pkg/depcache.cc 15 Dec 2004 19:07:55 -0000 1.1 +++ apt-pkg/depcache.cc 15 Dec 2004 18:36:01 -0000 @@ -72,6 +72,8 @@ // Find the proper cache slot StateCache &State = PkgState[I->ID]; State.iFlags = 0; + State.DirtyState = pkgCache::State::RemoveUnknown; + State.AutomaticRemove = I->AutomaticRemove; // Figure out the install version State.CandidateVer = GetCandidateVer(I); @@ -582,7 +584,8 @@ else P.Mode = ModeDelete; P.InstallVer = 0; - P.Flags &= Flag::Auto; + // This was not inverted before, but I think it should be + P.Flags &= ~Flag::Auto; AddStates(Pkg); Update(Pkg); @@ -754,6 +757,15 @@ AddSizes(Pkg); } /*}}}*/ +// DepCache::SetDirty - Switch the package between dirty states /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgDepCache::SetDirty(PkgIterator const &Pkg, pkgCache::State::PkgRemoveState To) +{ + StateCache &P = PkgState[Pkg->ID]; + P.DirtyState = To; +} + /*}}}*/ // DepCache::SetCandidateVersion - Change the candidate version /*{{{*/ // --------------------------------------------------------------------- /* */ Index: apt-pkg/depcache.h =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/depcache.h,v retrieving revision 1.1 diff -u -b -r1.1 depcache.h --- apt-pkg/depcache.h 15 Dec 2004 19:07:55 -0000 1.1 +++ apt-pkg/depcache.h 15 Dec 2004 18:36:01 -0000 @@ -79,6 +79,10 @@ unsigned short Flags; unsigned short iFlags; // Internal flags + // Traversal status and state for automatic removal + unsigned char DirtyState; + unsigned char AutomaticRemove; + // Various tree indicators signed char Status; // -1,0,1,2 unsigned char Mode; // ModeList @@ -99,6 +103,7 @@ inline bool NowBroken() const {return (DepState & DepNowMin) != DepNowMin;}; inline bool InstBroken() const {return (DepState & DepInstMin) != DepInstMin;}; inline bool Install() const {return Mode == ModeInstall;}; + inline unsigned char Dirty() const {return DirtyState;}; inline VerIterator InstVerIter(pkgCache &Cache) {return VerIterator(Cache,InstallVer);}; inline VerIterator CandidateVerIter(pkgCache &Cache) @@ -189,6 +194,7 @@ unsigned long Depth = 0); void SetReInstall(PkgIterator const &Pkg,bool To); void SetCandidateVersion(VerIterator TargetVer); + void SetDirty(PkgIterator const &Pkg, pkgCache::State::PkgRemoveState To); // This is for debuging void Update(OpProgress *Prog = 0); Index: apt-pkg/pkgcache.h =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/pkgcache.h,v retrieving revision 1.1 diff -u -b -r1.1 pkgcache.h --- apt-pkg/pkgcache.h 15 Dec 2004 19:07:56 -0000 1.1 +++ apt-pkg/pkgcache.h 15 Dec 2004 18:36:01 -0000 @@ -75,6 +75,7 @@ enum PkgInstState {Ok=0,ReInstReq=1,HoldInst=2,HoldReInstReq=3}; enum PkgCurrentState {NotInstalled=0,UnPacked=1,HalfConfigured=2, HalfInstalled=4,ConfigFiles=5,Installed=6}; + enum PkgRemoveState {RemoveUnknown=0, RemoveManual=1,RemoveSuggested=2,RemoveRecommended=3,RemoveRequired=4}; }; struct Flag @@ -199,6 +200,9 @@ unsigned char InstState; // Flags unsigned char CurrentState; // State + // Automatic deinstallation handling + unsigned char AutomaticRemove; + unsigned short ID; unsigned long Flags; }; Index: apt-pkg/tagfile.cc =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/tagfile.cc,v retrieving revision 1.1 diff -u -b -r1.1 tagfile.cc --- apt-pkg/tagfile.cc 15 Dec 2004 19:07:56 -0000 1.1 +++ apt-pkg/tagfile.cc 15 Dec 2004 18:36:01 -0000 @@ -383,6 +383,7 @@ "Version", "Revision", // Obsolete "Config-Version", // Obsolete + "Automatic-Remove", "Replaces", "Provides", "Depends", Index: apt-pkg/deb/deblistparser.cc =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/deb/deblistparser.cc,v retrieving revision 1.1 diff -u -b -r1.1 deblistparser.cc --- apt-pkg/deb/deblistparser.cc 15 Dec 2004 19:08:02 -0000 1.1 +++ apt-pkg/deb/deblistparser.cc 15 Dec 2004 18:36:01 -0000 @@ -136,6 +136,9 @@ if (ParseStatus(Pkg,Ver) == false) return false; + if (ParseRemove(Pkg) == false) + return false; + return true; } /*}}}*/ @@ -605,3 +608,34 @@ return Out; } /*}}}*/ +// ListParser::ParseRemove - Convert the automatic remove setting /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debListParser::ParseRemove(pkgCache::PkgIterator Pkg) +{ + const char *Start; + const char *Stop; + if (Section.Find("Automatic-Remove",Start,Stop) == false) + { + Pkg->AutomaticRemove = pkgCache::State::RemoveManual; + return true; + } + + // Isolate the word + const char *I = Start; + for(; I < Stop && *I != ' '; I++); + if (I != Stop) + return _error->Error("Malformed Automatic-Remove line"); + + WordList RemoveList[] = {{"manual",pkgCache::State::RemoveManual}, + {"suggested",pkgCache::State::RemoveSuggested}, + {"recommended",pkgCache::State::RemoveRecommended}, + {"required",pkgCache::State::RemoveRequired}, + {}}; + + if (GrabWord(string(Start,I-Start),RemoveList,Pkg->AutomaticRemove) == false) + return _error->Error("Unknown state in Automatic-Remove line"); + + return true; +} + /*}}}*/ Index: apt-pkg/deb/deblistparser.h =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/deb/deblistparser.h,v retrieving revision 1.1 diff -u -b -r1.1 deblistparser.h --- apt-pkg/deb/deblistparser.h 15 Dec 2004 19:08:02 -0000 1.1 +++ apt-pkg/deb/deblistparser.h 15 Dec 2004 18:36:01 -0000 @@ -37,6 +37,7 @@ bool ParseDepends(pkgCache::VerIterator Ver,const char *Tag, unsigned int Type); bool ParseProvides(pkgCache::VerIterator Ver); + bool ParseRemove(pkgCache::PkgIterator Pkg); static bool GrabWord(string Word,WordList *List,unsigned char &Out); public: Index: apt-pkg/deb/dpkgpm.cc =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/deb/dpkgpm.cc,v retrieving revision 1.1 diff -u -b -r1.1 dpkgpm.cc --- apt-pkg/deb/dpkgpm.cc 15 Dec 2004 19:08:03 -0000 1.1 +++ apt-pkg/deb/dpkgpm.cc 15 Dec 2004 18:40:02 -0000 @@ -53,6 +53,27 @@ return _error->Error("Internal Error, No file name for %s",Pkg.Name()); List.push_back(Item(Item::Install,Pkg,File)); + if (_config->FindB("APT::Get::AutomaticRemove")) + { + string NewAuto; + switch (Cache[Pkg].AutomaticRemove) + { + case pkgCache::State::RemoveManual: + NewAuto = "manual"; + break; + case pkgCache::State::RemoveRequired: + NewAuto = "required"; + break; + case pkgCache::State::RemoveRecommended: + NewAuto = "recommended"; + break; + case pkgCache::State::RemoveSuggested: + NewAuto = "suggested"; + break; + } + if (! NewAuto.empty()) + List.push_back(Item(Item::AutoRegister,Pkg,string(Pkg.Name()) + "/" + NewAuto)); + } return true; } /*}}}*/ @@ -83,6 +104,32 @@ return true; } /*}}}*/ +// DPkgPM::ResortList - Separate Install and AutoRegister /*{{{*/ +// --------------------------------------------------------------------- +/* Walk the list, and push the AutoRegisters behind the Installs */ +void pkgDPkgPM::SortList() +{ + vector ListInstall; + vector ListRegister; + + for (vector::iterator I = List.begin(); I != List.end(); ++I) + { + if (I->Op == Item::Install) + ListInstall.push_back(*I); + else if (I->Op == Item::AutoRegister) + ListRegister.push_back(*I); + else if (ListInstall.size() || ListRegister.size()) + { + copy(ListInstall.begin(), ListInstall.end(), I - (ListInstall.size() + ListRegister.size())); + copy(ListRegister.begin(), ListRegister.end(), I - ListRegister.size()); + ListInstall.clear(); + ListRegister.clear(); + } + } + copy(ListInstall.begin(), ListInstall.end(), List.end() - (ListInstall.size() + ListRegister.size())); + copy(ListRegister.begin(), ListRegister.end(), List.end() - ListRegister.size()); +} + /*}}}*/ // DPkgPM::RunScripts - Run a set of scripts /*{{{*/ // --------------------------------------------------------------------- /* This looks for a list of script sto run from the configuration file, @@ -222,6 +269,8 @@ if (I->Op == Item::Remove || I->Op == Item::Purge) fprintf(F,"**REMOVE**\n"); + if (I->Op == Item::AutoRegister) + fprintf(F,"**AUTOREGISTER**\n"); if (ferror(F) != 0) return false; @@ -328,6 +377,8 @@ /* This globs the operations and calls dpkg */ bool pkgDPkgPM::Go() { + SortList(); + unsigned int MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024); unsigned int MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024); @@ -396,6 +447,11 @@ Args[n++] = "--unpack"; Size += strlen(Args[n-1]); break; + + case Item::AutoRegister: + Args[n++] = "--set-remove"; + Size += strlen(Args[n-1]); + break; } // Write in the file or package names @@ -409,6 +465,14 @@ Size += strlen(Args[n-1]); } } + else if (I->Op == Item::AutoRegister) + { + for (;I != J && Size < MaxArgBytes; I++) + { + Args[n++] = I->File.c_str(); + Size += strlen(Args[n-1]); + } + } else { for (;I != J && Size < MaxArgBytes; I++) Index: apt-pkg/deb/dpkgpm.h =================================================================== RCS file: /home/mh21/cvs/apt/apt-pkg/deb/dpkgpm.h,v retrieving revision 1.1 diff -u -b -r1.1 dpkgpm.h --- apt-pkg/deb/dpkgpm.h 15 Dec 2004 19:08:03 -0000 1.1 +++ apt-pkg/deb/dpkgpm.h 15 Dec 2004 18:36:01 -0000 @@ -26,7 +26,7 @@ struct Item { - enum Ops {Install, Configure, Remove, Purge} Op; + enum Ops {Install, AutoRegister, Configure, Remove, Purge} Op; string File; PkgIterator Pkg; Item(Ops Op,PkgIterator Pkg,string File = "") : Op(Op), @@ -40,6 +40,7 @@ bool RunScripts(const char *Cnf); bool RunScriptsWithPkgs(const char *Cnf); bool SendV2Pkgs(FILE *F); + void SortList(); // The Actuall installation implementation virtual bool Install(PkgIterator Pkg,string File); Index: cmdline/apt-get.cc =================================================================== RCS file: /home/mh21/cvs/apt/cmdline/apt-get.cc,v retrieving revision 1.1 diff -u -b -r1.1 apt-get.cc --- cmdline/apt-get.cc 15 Dec 2004 19:08:03 -0000 1.1 +++ cmdline/apt-get.cc 15 Dec 2004 18:59:42 -0000 @@ -1375,6 +1375,125 @@ return InstallPackages(Cache,true); } /*}}}*/ +// RecurseDirty - Mark used packages as dirty /*{{{*/ +// --------------------------------------------------------------------- +/* Mark all reachable packages as dirty. */ +void RecurseDirty (CacheFile &Cache, pkgCache::PkgIterator Pkg, pkgCache::State::PkgRemoveState DirtLevel) +{ + // If it is not installed, and we are in manual mode, ignore it + if ((Pkg->CurrentVer == 0 && Cache[Pkg].Install() == false || Cache[Pkg].Delete() == true) && + DirtLevel == pkgCache::State::RemoveManual) + { +// fprintf(stdout,"This one is not installed/virtual %s %d %d\n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel); + return; + } + + // If it is not installed, and it is not virtual, ignore it + if ((Pkg->CurrentVer == 0 && Cache[Pkg].Install() == false || Cache[Pkg].Delete() == true) && + Pkg->VersionList != 0) + { +// fprintf(stdout,"This one is not installed %s %d %d\n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel); + return; + } + + // If it is similar or more dirty than we are ;-), because we've been here already, don't mark it + // This is necessary because virtual packages just relay the current level, + // so it may be possible e.g. that this was already seen with ::RemoveSuggested, but + // we are ::RemoveRequired + if (Cache[Pkg].Dirty() >= DirtLevel) + { +// fprintf(stdout,"Seen already %s %d %d\n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel); + return; + } + + // If it is less important than the current DirtLevel, don't mark it + if (Pkg->AutomaticRemove != pkgCache::State::RemoveManual && + Pkg->AutomaticRemove > DirtLevel) + { +// fprintf(stdout,"We don't need %s %d %d %d\n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel, Cache[Pkg].Dirty()); + return; + } + + // Mark it as used + Cache->SetDirty(Pkg, DirtLevel); + +// fprintf(stdout,"We keep %s %d %d\n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel); + + // We are a virtual package + if (Pkg->VersionList == 0) + { +// fprintf(stdout,"We are virtual %s %d %d\n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel); + for (pkgCache::PrvIterator Prv = Pkg.ProvidesList(); ! Prv.end(); ++Prv) + RecurseDirty (Cache, Prv.OwnerPkg(), DirtLevel); + return; + } + + // Depending on the type of dependency, follow it + for (pkgCache::DepIterator D = Cache[Pkg].InstVerIter(Cache).DependsList(); ! D.end(); ++D) + { +// fprintf(stdout,"We depend on %s %s\n", D.TargetPkg().Name(), D.DepType()); + + switch(D->Type) + { + case pkgCache::Dep::Depends: + case pkgCache::Dep::PreDepends: + RecurseDirty (Cache, D.TargetPkg(), pkgCache::State::RemoveRequired); + break; + case pkgCache::Dep::Recommends: + RecurseDirty (Cache, D.TargetPkg(), pkgCache::State::RemoveRecommended); + break; + case pkgCache::Dep::Suggests: + RecurseDirty (Cache, D.TargetPkg(), pkgCache::State::RemoveSuggested); + break; + case pkgCache::Dep::Conflicts: + case pkgCache::Dep::Replaces: + case pkgCache::Dep::Obsoletes: + // We don't handle these here + break; + } + } +// fprintf(stdout,"We keep %s %d %d \n", Pkg.Name(), Pkg->AutomaticRemove, DirtLevel); +} + /*}}}*/ +// DoAutomaticRemove - Remove all automatic unused packages /*{{{*/ +// --------------------------------------------------------------------- +/* Remove unused automatic packages */ +bool DoAutomaticRemove(CacheFile &Cache) +{ + if (_config->FindB("APT::Get::Remove",true) == false) + return _error->Error(_("We are not supposed to delete stuff, can't start AutoRemover")); + + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); ! Pkg.end(); ++Pkg) + Cache->SetDirty(Pkg, pkgCache::State::RemoveUnknown); + + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); ! Pkg.end(); ++Pkg) + RecurseDirty (Cache, Pkg, pkgCache::State::RemoveManual); + + for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); ! Pkg.end(); ++Pkg) + { + if (! Cache[Pkg].Dirty() && + (Pkg->CurrentVer != 0 && Cache[Pkg].Install() == false && Cache[Pkg].Delete() == false)) + { + fprintf(stdout,"We could delete %s %d\n", Pkg.Name(), Pkg->AutomaticRemove); + Cache->MarkDelete(Pkg,_config->FindB("APT::Get::Purge",false)); + } + } + + // Now see if we destroyed anything + if (Cache->BrokenCount() != 0) + { + c1out << _("Hmm, seems like the AutoRemover destroyed something which really\n" + "shouldn't happen. Please file a bug report against apt.") << endl; + c1out << endl; + c1out << _("The following information may help to resolve the situation:") << endl; + c1out << endl; + ShowBroken(c1out,Cache,false); + + return _error->Error(_("Internal Error, AutoRemover broke stuff")); + } + return true; +} + /*}}}*/ // DoInstall - Install packages from the command line /*{{{*/ // --------------------------------------------------------------------- /* Install named packages */ @@ -1546,6 +1665,10 @@ return _error->Error(_("Broken packages")); } + if (_config->FindB("APT::Get::AutomaticRemove")) { + if (! DoAutomaticRemove(Cache)) return false; + } + /* Print out a list of packages that are going to be installed extra to what the user asked */ if (Cache->InstCount() != ExpectedInst) @@ -1565,6 +1688,8 @@ if (*J == 0) { List += string(I.Name()) + " "; + if (_config->FindB("APT::Get::AutomaticRemove")) + Cache[I].AutomaticRemove = pkgCache::State::RemoveRequired; VersionsList += string(Cache[I].CandVersion) + "\n"; } } @@ -2410,6 +2535,7 @@ _config->Set("APT::Get::Fix-Broken",false); _config->Set("APT::Get::Force-Yes",false); _config->Set("APT::Get::List-Cleanup",true); + _config->Set("APT::Get::AutomaticRemove",false); } /*}}}*/ // SigWinch - Window size change signal handler /*{{{*/ @@ -2465,6 +2591,7 @@ {0,"remove","APT::Get::Remove",0}, {0,"only-source","APT::Get::Only-Source",0}, {0,"arch-only","APT::Get::Arch-Only",0}, + {0,"experimental-automatic-remove","APT::Get::AutomaticRemove",0}, {0,"allow-unauthenticated","APT::Get::AllowUnauthenticated",0}, {'c',"config-file",0,CommandLine::ConfigFile}, {'o',"option",0,CommandLine::ArbItem},