diff options
Diffstat (limited to 'apt-pkg')
50 files changed, 3966 insertions, 276 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 0c1aad485..3d05e62ae 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -21,6 +21,8 @@ #include <apt-pkg/strutl.h> #include <apt-pkg/fileutl.h> #include <apt-pkg/md5.h> +#include <apt-pkg/sha1.h> +#include <apt-pkg/tagfile.h> #include <apti18n.h> @@ -28,6 +30,7 @@ #include <unistd.h> #include <errno.h> #include <string> +#include <sstream> #include <stdio.h> /*}}}*/ @@ -97,7 +100,8 @@ void pkgAcquire::Item::Done(string Message,unsigned long Size,string, { // We just downloaded something.. string FileName = LookupTag(Message,"Filename"); - if (Complete == false && FileName == DestFile) + // we only inform the Log class if it was actually not a local thing + if (Complete == false && !Local && FileName == DestFile) { if (Owner->Log != 0) Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str())); @@ -128,14 +132,432 @@ void pkgAcquire::Item::Rename(string From,string To) } /*}}}*/ + +// AcqDiffIndex::AcqDiffIndex - Constructor +// --------------------------------------------------------------------- +/* Get the DiffIndex file first and see if there are patches availabe + * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the + * patches. If anything goes wrong in that process, it will fall back to + * the original packages file + */ +pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner, + string URI,string URIDesc,string ShortDesc, + string ExpectedMD5) + : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), Description(URIDesc) +{ + + Debug = _config->FindB("Debug::pkgAcquire::Diffs",false); + + Desc.Description = URIDesc + "/DiffIndex"; + Desc.Owner = this; + Desc.ShortDesc = ShortDesc; + Desc.URI = URI + ".diff/Index"; + + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(URI) + string(".DiffIndex"); + + if(Debug) + std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl; + + // look for the current package file + CurrentPackagesFile = _config->FindDir("Dir::State::lists"); + CurrentPackagesFile += URItoFileName(RealURI); + + // FIXME: this file:/ check is a hack to prevent fetching + // from local sources. this is really silly, and + // should be fixed cleanly as soon as possible + if(!FileExists(CurrentPackagesFile) || + Desc.URI.substr(0,strlen("file:/")) == "file:/") + { + // we don't have a pkg file or we don't want to queue + if(Debug) + std::clog << "No index file, local or canceld by user" << std::endl; + Failed("", NULL); + return; + } + + if(Debug) + std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): " + << CurrentPackagesFile << std::endl; + + QueueURI(Desc); + +} + +// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/ +// --------------------------------------------------------------------- +/* The only header we use is the last-modified header. */ +string pkgAcqDiffIndex::Custom600Headers() +{ + string Final = _config->FindDir("Dir::State::lists"); + Final += URItoFileName(RealURI) + string(".IndexDiff"); + + if(Debug) + std::clog << "Custom600Header-IMS: " << Final << std::endl; + + struct stat Buf; + if (stat(Final.c_str(),&Buf) != 0) + return "\nIndex-File: true"; + + return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); +} + + +bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) +{ + if(Debug) + std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile + << std::endl; + + pkgTagSection Tags; + string ServerSha1; + vector<DiffInfo> available_patches; + + FileFd Fd(IndexDiffFile,FileFd::ReadOnly); + pkgTagFile TF(&Fd); + if (_error->PendingError() == true) + return false; + + if(TF.Step(Tags) == true) + { + string local_sha1; + bool found = false; + DiffInfo d; + string size; + + string tmp = Tags.FindS("SHA1-Current"); + std::stringstream ss(tmp); + ss >> ServerSha1; + + FileFd fd(CurrentPackagesFile, FileFd::ReadOnly); + SHA1Summation SHA1; + SHA1.AddFD(fd.Fd(), fd.Size()); + local_sha1 = string(SHA1.Result()); + + if(local_sha1 == ServerSha1) + { + // we have the same sha1 as the server + if(Debug) + std::clog << "Package file is up-to-date" << std::endl; + // set found to true, this will queue a pkgAcqIndexDiffs with + // a empty availabe_patches + found = true; + } + else + { + if(Debug) + std::clog << "SHA1-Current: " << ServerSha1 << std::endl; + + // check the historie and see what patches we need + string history = Tags.FindS("SHA1-History"); + std::stringstream hist(history); + while(hist >> d.sha1 >> size >> d.file) + { + d.size = atoi(size.c_str()); + // read until the first match is found + if(d.sha1 == local_sha1) + found=true; + // from that point on, we probably need all diffs + if(found) + { + if(Debug) + std::clog << "Need to get diff: " << d.file << std::endl; + available_patches.push_back(d); + } + } + } + + // no information how to get the patches, bail out + if(!found) + { + if(Debug) + std::clog << "Can't find a patch in the index file" << std::endl; + // Failed will queue a big package file + Failed("", NULL); + } + else + { + // queue the diffs + new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc, + ExpectedMD5, available_patches); + Complete = false; + Status = StatDone; + Dequeue(); + return true; + } + } + + return false; +} + +void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl + << "Falling back to normal index file aquire" << std::endl; + + new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc, + ExpectedMD5); + + Complete = false; + Status = StatDone; + Dequeue(); +} + +void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, + pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl; + + Item::Done(Message,Size,Md5Hash,Cnf); + + string FinalFile; + FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI); + + // sucess in downloading the index + // rename the index + FinalFile += string(".IndexDiff"); + if(Debug) + std::clog << "Renaming: " << DestFile << " -> " << FinalFile + << std::endl; + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + DestFile = FinalFile; + + if(!ParseDiffIndex(DestFile)) + return Failed("", NULL); + + Complete = true; + Status = StatDone; + Dequeue(); + return; +} + + + +// AcqIndexDiffs::AcqIndexDiffs - Constructor +// --------------------------------------------------------------------- +/* The package diff is added to the queue. one object is constructed + * for each diff and the index + */ +pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner, + string URI,string URIDesc,string ShortDesc, + string ExpectedMD5, vector<DiffInfo> diffs) + : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), + available_patches(diffs) +{ + + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(URI); + + Debug = _config->FindB("Debug::pkgAcquire::Diffs",false); + + Desc.Description = URIDesc; + Desc.Owner = this; + Desc.ShortDesc = ShortDesc; + + if(available_patches.size() == 0) + { + // we are done (yeah!) + Finish(true); + } + else + { + // get the next diff + State = StateFetchDiff; + QueueNextDiff(); + } +} + + +void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl + << "Falling back to normal index file aquire" << std::endl; + new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc, + ExpectedMD5); + Finish(); +} + + +// helper that cleans the item out of the fetcher queue +void pkgAcqIndexDiffs::Finish(bool allDone) +{ + // we restore the original name, this is required, otherwise + // the file will be cleaned + if(allDone) + { + DestFile = _config->FindDir("Dir::State::lists"); + DestFile += URItoFileName(RealURI); + + // do the final md5sum checking + MD5Summation sum; + FileFd Fd(DestFile, FileFd::ReadOnly); + sum.AddFD(Fd.Fd(), Fd.Size()); + Fd.Close(); + string MD5 = (string)sum.Result(); + + if (!ExpectedMD5.empty() && MD5 != ExpectedMD5) + { + Status = StatAuthError; + ErrorText = _("MD5Sum mismatch"); + Rename(DestFile,DestFile + ".FAILED"); + Dequeue(); + return; + } + + // this is for the "real" finish + Complete = true; + Status = StatDone; + Dequeue(); + if(Debug) + std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl; + return; + } + + if(Debug) + std::clog << "Finishing: " << Desc.URI << std::endl; + Complete = false; + Status = StatDone; + Dequeue(); + return; +} + + + +bool pkgAcqIndexDiffs::QueueNextDiff() +{ + + // calc sha1 of the just patched file + string FinalFile = _config->FindDir("Dir::State::lists"); + FinalFile += URItoFileName(RealURI); + + FileFd fd(FinalFile, FileFd::ReadOnly); + SHA1Summation SHA1; + SHA1.AddFD(fd.Fd(), fd.Size()); + string local_sha1 = string(SHA1.Result()); + if(Debug) + std::clog << "QueueNextDiff: " + << FinalFile << " (" << local_sha1 << ")"<<std::endl; + + // remove all patches until the next matching patch is found + // this requires the Index file to be ordered + for(vector<DiffInfo>::iterator I=available_patches.begin(); + available_patches.size() > 0 && + I != available_patches.end() && + (*I).sha1 != local_sha1; + I++) + { + available_patches.erase(I); + } + + // error checking and falling back if no patch was found + if(available_patches.size() == 0) + { + Failed("", NULL); + return false; + } + + // queue the right diff + Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz"; + Desc.Description = available_patches[0].file + string(".pdiff"); + + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file); + + if(Debug) + std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl; + + QueueURI(Desc); + + return true; +} + + + +void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, + pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl; + + Item::Done(Message,Size,Md5Hash,Cnf); + + string FinalFile; + FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI); + + // sucess in downloading a diff, enter ApplyDiff state + if(State == StateFetchDiff) + { + + if(Debug) + std::clog << "Sending to gzip method: " << FinalFile << std::endl; + + string FileName = LookupTag(Message,"Filename"); + State = StateUnzipDiff; + Local = true; + Desc.URI = "gzip:" + FileName; + DestFile += ".decomp"; + QueueURI(Desc); + Mode = "gzip"; + return; + } + + // sucess in downloading a diff, enter ApplyDiff state + if(State == StateUnzipDiff) + { + + // rred excepts the patch as $FinalFile.ed + Rename(DestFile,FinalFile+".ed"); + + if(Debug) + std::clog << "Sending to rred method: " << FinalFile << std::endl; + + State = StateApplyDiff; + Local = true; + Desc.URI = "rred:" + FinalFile; + QueueURI(Desc); + Mode = "rred"; + return; + } + + + // success in download/apply a diff, queue next (if needed) + if(State == StateApplyDiff) + { + // remove the just applied patch + available_patches.erase(available_patches.begin()); + + // move into place + if(Debug) + { + std::clog << "Moving patched file in place: " << std::endl + << DestFile << " -> " << FinalFile << std::endl; + } + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + + // see if there is more to download + if(available_patches.size() > 0) { + new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc, + ExpectedMD5, available_patches); + return Finish(); + } else + return Finish(true); + } +} + + // AcqIndex::AcqIndex - Constructor /*{{{*/ // --------------------------------------------------------------------- /* The package file is added to the queue and a second class is instantiated to fetch the revision file */ pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, string URI,string URIDesc,string ShortDesc, - string ExpectedMD5, string comprExt) : - Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5) + string ExpectedMD5, string comprExt) + : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5) { Decompression = false; Erase = false; @@ -180,6 +602,7 @@ string pkgAcqIndex::Custom600Headers() void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) { + // no .bz2 found, retry with .gz if(Desc.URI.substr(Desc.URI.size()-3) == "bz2") { Desc.URI = Desc.URI.substr(0,Desc.URI.size()-3) + "gz"; @@ -191,9 +614,15 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Complete = false; Dequeue(); return; + } + + // on decompression failure, remove bad versions in partial/ + if(Decompression && Erase) { + string s = _config->FindDir("Dir::State::lists") + "partial/"; + s += URItoFileName(RealURI); + unlink(s.c_str()); } - Item::Failed(Message,Cnf); } @@ -305,6 +734,35 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5, Mode = decompProg; } +// AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* The Translation file is added to the queue */ +pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, + string URI,string URIDesc,string ShortDesc) : + pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, "", "") +{ +} + + /*}}}*/ +// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf) +{ + if (Cnf->LocalOnly == true || + StringToBool(LookupTag(Message,"Transient-Failure"),false) == false) + { + // Ignore this + Status = StatDone; + Complete = false; + Dequeue(); + return; + } + + Item::Failed(Message,Cnf); +} + /*}}}*/ + pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, string URI,string URIDesc,string ShortDesc, string MetaIndexURI, string MetaIndexURIDesc, @@ -318,8 +776,9 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, DestFile = _config->FindDir("Dir::State::lists") + "partial/"; DestFile += URItoFileName(URI); - // remove any partial downloaded sig-file. it may confuse proxies - // and is too small to warrant a partial download anyway + // remove any partial downloaded sig-file in partial/. + // it may confuse proxies and is too small to warrant a + // partial download anyway unlink(DestFile.c_str()); // Create the item @@ -386,17 +845,22 @@ void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5, /*}}}*/ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) { + string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); // if we get a network error we fail gracefully - if(LookupTag(Message,"FailReason") == "Timeout" || - LookupTag(Message,"FailReason") == "TmpResolveFailure" || - LookupTag(Message,"FailReason") == "ConnectionRefused") { + if(Status == StatTransientNetworkError) + { Item::Failed(Message,Cnf); + // move the sigfile back on transient network failures + if(FileExists(DestFile)) + Rename(DestFile,Final); + + // set the status back to , Item::Failed likes to reset it + Status = pkgAcquire::Item::StatTransientNetworkError; return; } // Delete any existing sigfile when the acquire failed - string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI); unlink(Final.c_str()); // queue a pkgAcqMetaIndex with no sigfile @@ -529,6 +993,7 @@ void pkgAcqMetaIndex::RetrievalDone(string Message) // Move it into position Rename(DestFile,FinalFile); } + chmod(FinalFile.c_str(),0644); DestFile = FinalFile; } @@ -598,9 +1063,14 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify) } } - // Queue Packages file - new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description, - (*Target)->ShortDesc, ExpectedIndexMD5); + // Queue Packages file (either diff or full packages files, depending + // on the users option) + if(_config->FindB("Acquire::PDiffs",true) == true) + new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description, + (*Target)->ShortDesc, ExpectedIndexMD5); + else + new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description, + (*Target)->ShortDesc, ExpectedIndexMD5); } } @@ -634,7 +1104,7 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) // check for missing sigs (that where not fatal because otherwise we had // bombed earlier) string missingkeys; - string msg = _("There are no public key available for the " + string msg = _("There is no public key available for the " "following key IDs:\n"); pos = Message.find("NO_PUBKEY "); if (pos != std::string::npos) diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index b47e198d8..f5272ed86 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -9,8 +9,8 @@ the Owner Acquire class. Derived classes will then call QueueURI to register all the URI's they wish to fetch at the initial moment. - Two item classes are provided to provide functionality for downloading - of Index files and downloading of Packages. + Three item classes are provided to provide functionality for + downloading of Index, Translation and Packages files. A Archive class is provided for downloading .deb files. It does Md5 checking and source location as well as a retry algorithm. @@ -28,67 +28,491 @@ #include <apt-pkg/indexrecords.h> -// Item to acquire +/** \addtogroup acquire + * @{ + * + * \file acquire-item.h + */ + +/** \brief Represents the process by which a pkgAcquire object should + * retrieve a file or a collection of files. + * + * By convention, Item subclasses should insert themselves into the + * acquire queue when they are created by calling QueueURI(), and + * remove themselves by calling Dequeue() when either Done() or + * Failed() is invoked. Item objects are also responsible for + * notifying the download progress indicator (accessible via + * #Owner->Log) of their status. + * + * \see pkgAcquire + */ class pkgAcquire::Item { protected: - // Some private helper methods for registering URIs + /** \brief The acquire object with which this item is associated. */ pkgAcquire *Owner; + + /** \brief Insert this item into its owner's queue. + * + * \param ItemDesc Metadata about this item (its URI and + * description). + */ inline void QueueURI(ItemDesc &Item) {Owner->Enqueue(Item);}; + + /** \brief Remove this item from its owner's queue. */ inline void Dequeue() {Owner->Dequeue(this);}; - // Safe rename function with timestamp preservation + /** \brief Rename a file without modifying its timestamp. + * + * Many item methods call this as their final action. + * + * \param From The file to be renamed. + * + * \param To The new name of #From. If #To exists it will be + * overwritten. + */ void Rename(string From,string To); public: - // State of the item - enum {StatIdle, StatFetching, StatDone, StatError, StatAuthError} Status; + /** \brief The current status of this item. */ + enum ItemState + { + /** \brief The item is waiting to be downloaded. */ + StatIdle, + + /** \brief The item is currently being downloaded. */ + StatFetching, + + /** \brief The item has been successfully downloaded. */ + StatDone, + + /** \brief An error was encountered while downloading this + * item. + */ + StatError, + + /** \brief The item was downloaded but its authenticity could + * not be verified. + */ + StatAuthError, + + /** \brief The item was could not be downloaded because of + * a transient network error (e.g. network down) + */ + StatTransientNetworkError + } Status; + + /** \brief Contains a textual description of the error encountered + * if #Status is #StatError or #StatAuthError. + */ string ErrorText; + + /** \brief The size of the object to fetch. */ unsigned long FileSize; - unsigned long PartialSize; + + /** \brief How much of the object was already fetched. */ + unsigned long PartialSize; + + /** \brief If not \b NULL, contains the name of a subprocess that + * is operating on this object (for instance, "gzip" or "gpgv"). + */ const char *Mode; + + /** \brief A client-supplied unique identifier. + * + * This field is initalized to 0; it is meant to be filled in by + * clients that wish to use it to uniquely identify items. + * + * \todo it's unused in apt itself + */ unsigned long ID; + + /** \brief If \b true, the entire object has been successfully fetched. + * + * Subclasses should set this to \b true when appropriate. + */ bool Complete; + + /** \brief If \b true, the URI of this object is "local". + * + * The only effect of this field is to exclude the object from the + * download progress indicator's overall statistics. + */ bool Local; - // Number of queues we are inserted into + /** \brief The number of fetch queues into which this item has been + * inserted. + * + * There is one queue for each source from which an item could be + * downloaded. + * + * \sa pkgAcquire + */ unsigned int QueueCounter; - // File to write the fetch into + /** \brief The name of the file into which the retrieved object + * will be written. + */ string DestFile; - // Action members invoked by the worker + /** \brief Invoked by the acquire worker when the object couldn't + * be fetched. + * + * This is a branch of the continuation of the fetch process. + * + * \param Message An RFC822-formatted message from the acquire + * method describing what went wrong. Use LookupTag() to parse + * it. + * + * \param Cnf The method via which the worker tried to fetch this object. + * + * \sa pkgAcqMethod + */ virtual void Failed(string Message,pkgAcquire::MethodConfig *Cnf); + + /** \brief Invoked by the acquire worker when the object was + * fetched successfully. + * + * Note that the object might \e not have been written to + * DestFile; check for the presence of an Alt-Filename entry in + * Message to find the file to which it was really written. + * + * Done is often used to switch from one stage of the processing + * to the next (e.g. fetching, unpacking, copying). It is one + * branch of the continuation of the fetch process. + * + * \param Message Data from the acquire method. Use LookupTag() + * to parse it. + * \param Size The size of the object that was fetched. + * \param Md5Hash The MD5Sum of the object that was fetched. + * \param Cnf The method via which the object was fetched. + * + * \sa pkgAcqMethod + */ virtual void Done(string Message,unsigned long Size,string Md5Hash, pkgAcquire::MethodConfig *Cnf); + + /** \brief Invoked when the worker starts to fetch this object. + * + * \param Message RFC822-formatted data from the worker process. + * Use LookupTag() to parse it. + * + * \param Size The size of the object being fetched. + * + * \sa pkgAcqMethod + */ virtual void Start(string Message,unsigned long Size); + + /** \brief Custom headers to be sent to the fetch process. + * + * \return a string containing RFC822-style headers that are to be + * inserted into the 600 URI Acquire message sent to the fetch + * subprocess. The headers are inserted after a newline-less + * line, so they should (if nonempty) have a leading newline and + * no trailing newline. + */ virtual string Custom600Headers() {return string();}; + + /** \brief A "descriptive" URI-like string. + * + * \return a URI that should be used to describe what is being fetched. + */ virtual string DescURI() = 0; + /** \brief Short item description. + * + * \return a brief description of the object being fetched. + */ virtual string ShortDesc() {return DescURI();} + + /** \brief Invoked by the worker when the download is completely done. */ virtual void Finished() {}; - // Inquire functions + /** \brief MD5Sum. + * + * \return the MD5Sum of this object, if applicable; otherwise, an + * empty string. + */ virtual string MD5Sum() {return string();}; + + /** \return the acquire process with which this item is associated. */ pkgAcquire *GetOwner() {return Owner;}; + + /** \return \b true if this object is being fetched from a trusted source. */ virtual bool IsTrusted() {return false;}; - + + /** \brief Initialize an item. + * + * Adds the item to the list of items known to the acquire + * process, but does not place it into any fetch queues (you must + * manually invoke QueueURI() to do so). + * + * Initializes all fields of the item other than Owner to 0, + * false, or the empty string. + * + * \param Owner The new owner of this item. + */ Item(pkgAcquire *Owner); + + /** \brief Remove this item from its owner's queue by invoking + * pkgAcquire::Remove. + */ virtual ~Item(); }; -// Item class for index files -class pkgAcqIndex : public pkgAcquire::Item +/** \brief Information about an index patch (aka diff). */ +struct DiffInfo { + /** The filename of the diff. */ + string file; + + /** The sha1 hash of the diff. */ + string sha1; + + /** The size of the diff. */ + unsigned long size; +}; + +/** \brief An item that is responsible for fetching an index file of + * package list diffs and starting the package list's download. + * + * This item downloads the Index file and parses it, then enqueues + * additional downloads of either the individual patches (using + * pkgAcqIndexDiffs) or the entire Packages file (using pkgAcqIndex). + * + * \sa pkgAcqIndexDiffs, pkgAcqIndex + */ +class pkgAcqDiffIndex : public pkgAcquire::Item { + protected: + /** \brief If \b true, debugging information will be written to std::clog. */ + bool Debug; + + /** \brief The item that is currently being downloaded. */ + pkgAcquire::ItemDesc Desc; + + /** \brief The URI of the index file to recreate at our end (either + * by downloading it or by applying partial patches). + */ + string RealURI; + + /** \brief The MD5Sum that the real index file should have after + * all patches have been applied. + */ + string ExpectedMD5; + + /** \brief The index file which will be patched to generate the new + * file. + */ + string CurrentPackagesFile; + + /** \brief A description of the Packages file (stored in + * pkgAcquire::ItemDesc::Description). + */ + string Description; + + public: + // Specialized action members + virtual void Failed(string Message,pkgAcquire::MethodConfig *Cnf); + virtual void Done(string Message,unsigned long Size,string Md5Hash, + pkgAcquire::MethodConfig *Cnf); + virtual string DescURI() {return RealURI + "Index";}; + virtual string Custom600Headers(); + + /** \brief Parse the Index file for a set of Packages diffs. + * + * Parses the Index file and creates additional download items as + * necessary. + * + * \param IndexDiffFile The name of the Index file. + * + * \return \b true if the Index file was successfully parsed, \b + * false otherwise. + */ + bool ParseDiffIndex(string IndexDiffFile); + + + /** \brief Create a new pkgAcqDiffIndex. + * + * \param Owner The Acquire object that owns this item. + * + * \param URI The URI of the list file to download. + * + * \param URIDesc A long description of the list file to download. + * + * \param ShortDesc A short description of the list file to download. + * + * \param ExpectedMD5 The list file's MD5 signature. + */ + pkgAcqDiffIndex(pkgAcquire *Owner,string URI,string URIDesc, + string ShortDesc, string ExpectedMD5); +}; + +/** \brief An item that is responsible for fetching all the patches + * that need to be applied to a given package index file. + * + * After downloading and applying a single patch, this item will + * enqueue a new pkgAcqIndexDiffs to download and apply the remaining + * patches. If no patch can be found that applies to an intermediate + * file or if one of the patches cannot be downloaded, falls back to + * downloading the entire package index file using pkgAcqIndex. + * + * \sa pkgAcqDiffIndex, pkgAcqIndex + */ +class pkgAcqIndexDiffs : public pkgAcquire::Item +{ + private: + + /** \brief Queue up the next diff download. + * + * Search for the next available diff that applies to the file + * that currently exists on disk, and enqueue it by calling + * QueueURI(). + * + * \return \b true if an applicable diff was found, \b false + * otherwise. + */ + bool QueueNextDiff(); + + /** \brief Handle tasks that must be performed after the item + * finishes downloading. + * + * Dequeues the item and checks the resulting file's md5sum + * against ExpectedMD5 after the last patch was applied. + * There is no need to check the md5/sha1 after a "normal" + * patch because QueueNextDiff() will check the sha1 later. + * + * \param allDone If \b true, the file was entirely reconstructed, + * and its md5sum is verified. + */ + void Finish(bool allDone=false); + protected: + + /** \brief If \b true, debugging output will be written to + * std::clog. + */ + bool Debug; + + /** \brief A description of the item that is currently being + * downloaded. + */ + pkgAcquire::ItemDesc Desc; + + /** \brief The URI of the package index file that is being + * reconstructed. + */ + string RealURI; + + /** \brief The MD5Sum of the package index file that is being + * reconstructed. + */ + string ExpectedMD5; + + /** A description of the file being downloaded. */ + string Description; + + /** The patches that remain to be downloaded, including the patch + * being downloaded right now. This list should be ordered so + * that each diff appears before any diff that depends on it. + * + * \todo These are indexed by sha1sum; why not use some sort of + * dictionary instead of relying on ordering and stripping them + * off the front? + */ + vector<DiffInfo> available_patches; + /** The current status of this patch. */ + enum DiffState + { + /** \brief The diff is in an unknown state. */ + StateFetchUnkown, + + /** \brief The diff is currently being fetched. */ + StateFetchDiff, + + /** \brief The diff is currently being uncompressed. */ + StateUnzipDiff, + + /** \brief The diff is currently being applied. */ + StateApplyDiff + } State; + + public: + /** \brief Called when the patch file failed to be downloaded. + * + * This method will fall back to downloading the whole index file + * outright; its arguments are ignored. + */ + virtual void Failed(string Message,pkgAcquire::MethodConfig *Cnf); + + virtual void Done(string Message,unsigned long Size,string Md5Hash, + pkgAcquire::MethodConfig *Cnf); + virtual string DescURI() {return RealURI + "Index";}; + + /** \brief Create an index diff item. + * + * After filling in its basic fields, this invokes Finish(true) if + * #diffs is empty, or QueueNextDiff() otherwise. + * + * \param Owner The pkgAcquire object that owns this item. + * + * \param URI The URI of the package index file being + * reconstructed. + * + * \param URIDesc A long description of this item. + * + * \param ShortDesc A brief description of this item. + * + * \param ExpectedMD5 The expected md5sum of the completely + * reconstructed package index file; the index file will be tested + * against this value when it is entirely reconstructed. + * + * \param diffs The remaining diffs from the index of diffs. They + * should be ordered so that each diff appears before any diff + * that depends on it. + */ + pkgAcqIndexDiffs(pkgAcquire *Owner,string URI,string URIDesc, + string ShortDesc, string ExpectedMD5, + vector<DiffInfo> diffs=vector<DiffInfo>()); +}; + +/** \brief An acquire item that is responsible for fetching an index + * file (e.g., Packages or Sources). + * + * \sa pkgAcqDiffIndex, pkgAcqIndexDiffs, pkgAcqIndexTrans + * + * \todo Why does pkgAcqIndex have protected members? + */ +class pkgAcqIndex : public pkgAcquire::Item +{ + protected: + + /** \brief If \b true, the index file has been decompressed. */ bool Decompression; + + /** \brief If \b true, the partially downloaded file will be + * removed when the download completes. + */ bool Erase; + + /** \brief The download request that is currently being + * processed. + */ pkgAcquire::ItemDesc Desc; + + /** \brief The object that is actually being fetched (minus any + * compression-related extensions). + */ string RealURI; + + /** \brief The expected md5sum of the decompressed index file. */ string ExpectedMD5; + + /** \brief The compression-related file extension that is being + * added to the downloaded file (e.g., ".gz" or ".bz2"). + */ string CompressionExtension; public: @@ -100,26 +524,120 @@ class pkgAcqIndex : public pkgAcquire::Item virtual string Custom600Headers(); virtual string DescURI() {return RealURI + CompressionExtension;}; + /** \brief Create a pkgAcqIndex. + * + * \param Owner The pkgAcquire object with which this item is + * associated. + * + * \param URI The URI of the index file that is to be downloaded. + * + * \param URIDesc A "URI-style" description of this index file. + * + * \param ShortDesc A brief description of this index file. + * + * \param ExpectedMD5 The expected md5sum of this index file. + * + * \param compressExt The compression-related extension with which + * this index file should be downloaded, or "" to autodetect + * (".bz2" is used if bzip2 is installed, ".gz" otherwise). + */ pkgAcqIndex(pkgAcquire *Owner,string URI,string URIDesc, - string ShortDesct, string ExpectedMD5, string compressExt=""); + string ShortDesc, string ExpectedMD5, string compressExt=""); +}; + +/** \brief An acquire item that is responsible for fetching a + * translated index file. + * + * The only difference from pkgAcqIndex is that transient failures + * are suppressed: no error occurs if the translated index file is + * missing. + */ +class pkgAcqIndexTrans : public pkgAcqIndex +{ + public: + + virtual void Failed(string Message,pkgAcquire::MethodConfig *Cnf); + + /** \brief Create a pkgAcqIndexTrans. + * + * \param Owner The pkgAcquire object with which this item is + * associated. + * + * \param URI The URI of the index file that is to be downloaded. + * + * \param URIDesc A "URI-style" description of this index file. + * + * \param ShortDesc A brief description of this index file. + * + * \param ExpectedMD5 The expected md5sum of this index file. + * + * \param compressExt The compression-related extension with which + * this index file should be downloaded, or "" to autodetect + * (".bz2" is used if bzip2 is installed, ".gz" otherwise). + */ + pkgAcqIndexTrans(pkgAcquire *Owner,string URI,string URIDesc, + string ShortDesc); }; +/** \brief Information about an index file. */ struct IndexTarget { + /** \brief A URI from which the index file can be downloaded. */ string URI; + + /** \brief A description of the index file. */ string Description; + + /** \brief A shorter description of the index file. */ string ShortDesc; + + /** \brief The key by which this index file should be + * looked up within the meta signature file. + */ string MetaKey; }; -// Item class for index signatures +/** \brief An acquire item that downloads the detached signature + * of a meta-index (Release) file, then queues up the release + * file itself. + * + * \todo Why protected members? + * + * \sa pkgAcqMetaIndex + */ class pkgAcqMetaSig : public pkgAcquire::Item { protected: - + /** \brief The fetch request that is currently being processed. */ pkgAcquire::ItemDesc Desc; - string RealURI,MetaIndexURI,MetaIndexURIDesc,MetaIndexShortDesc; + + /** \brief The URI of the signature file. Unlike Desc.URI, this is + * never modified; it is used to determine the file that is being + * downloaded. + */ + string RealURI; + + /** \brief The URI of the meta-index file to be fetched after the signature. */ + string MetaIndexURI; + + /** \brief A "URI-style" description of the meta-index file to be + * fetched after the signature. + */ + string MetaIndexURIDesc; + + /** \brief A brief description of the meta-index file to be fetched + * after the signature. + */ + string MetaIndexShortDesc; + + /** \brief A package-system-specific parser for the meta-index file. */ indexRecords* MetaIndexParser; + + /** \brief The index files which should be looked up in the meta-index + * and then downloaded. + * + * \todo Why a list of pointers instead of a list of structs? + */ const vector<struct IndexTarget*>* IndexTargets; public: @@ -131,29 +649,90 @@ class pkgAcqMetaSig : public pkgAcquire::Item virtual string Custom600Headers(); virtual string DescURI() {return RealURI; }; + /** \brief Create a new pkgAcqMetaSig. */ pkgAcqMetaSig(pkgAcquire *Owner,string URI,string URIDesc, string ShortDesc, string MetaIndexURI, string MetaIndexURIDesc, string MetaIndexShortDesc, const vector<struct IndexTarget*>* IndexTargets, indexRecords* MetaIndexParser); }; -// Item class for index signatures +/** \brief An item that is responsible for downloading the meta-index + * file (i.e., Release) itself and verifying its signature. + * + * Once the download and verification are complete, the downloads of + * the individual index files are queued up using pkgAcqDiffIndex. + * If the meta-index file had a valid signature, the expected md5sums + * of the index files will be the md5sums listed in the meta-index; + * otherwise, the expected md5sums will be "" (causing the + * authentication of the index files to be bypassed). + */ class pkgAcqMetaIndex : public pkgAcquire::Item { protected: - + /** \brief The fetch command that is currently being processed. */ pkgAcquire::ItemDesc Desc; - string RealURI; // FIXME: is this redundant w/ Desc.URI? + + /** \brief The URI that is actually being downloaded; never + * modified by pkgAcqMetaIndex. + */ + string RealURI; + + /** \brief The file in which the signature for this index was stored. + * + * If empty, the signature and the md5sums of the individual + * indices will not be checked. + */ string SigFile; + + /** \brief The index files to download. */ const vector<struct IndexTarget*>* IndexTargets; + + /** \brief The parser for the meta-index file. */ indexRecords* MetaIndexParser; + + /** \brief If \b true, the index's signature is currently being verified. + */ bool AuthPass; // required to deal gracefully with problems caused by incorrect ims hits bool IMSHit; + /** \brief Check that the release file is a release file for the + * correct distribution. + * + * \return \b true if no fatal errors were encountered. + */ bool VerifyVendor(string Message); + + /** \brief Called when a file is finished being retrieved. + * + * If the file was not downloaded to DestFile, a copy process is + * set up to copy it to DestFile; otherwise, Complete is set to \b + * true and the file is moved to its final location. + * + * \param Message The message block received from the fetch + * subprocess. + */ void RetrievalDone(string Message); + + /** \brief Called when authentication succeeded. + * + * Sanity-checks the authenticated file, queues up the individual + * index files for download, and saves the signature in the lists + * directory next to the authenticated list file. + * + * \param Message The message block received from the fetch + * subprocess. + */ void AuthDone(string Message); + + /** \brief Starts downloading the individual index files. + * + * \param verify If \b true, only indices whose expected md5sum + * can be determined from the meta-index will be downloaded, and + * the md5sums of indices will be checked (reporting + * #StatAuthError if there is a mismatch). If verify is \b false, + * no md5sum checking will be performed. + */ void QueueIndexes(bool verify); public: @@ -165,6 +744,7 @@ class pkgAcqMetaIndex : public pkgAcquire::Item virtual string Custom600Headers(); virtual string DescURI() {return RealURI; }; + /** \brief Create a new pkgAcqMetaIndex. */ pkgAcqMetaIndex(pkgAcquire *Owner, string URI,string URIDesc, string ShortDesc, string SigFile, @@ -172,28 +752,58 @@ class pkgAcqMetaIndex : public pkgAcquire::Item indexRecords* MetaIndexParser); }; -// Item class for archive files +/** \brief An item that is responsible for fetching a package file. + * + * If the package file already exists in the cache, nothing will be + * done. + */ class pkgAcqArchive : public pkgAcquire::Item { protected: - - // State information for the retry mechanism + /** \brief The package version being fetched. */ pkgCache::VerIterator Version; + + /** \brief The fetch command that is currently being processed. */ pkgAcquire::ItemDesc Desc; + + /** \brief The list of sources from which to pick archives to + * download this package from. + */ pkgSourceList *Sources; + + /** \brief A package records object, used to look up the file + * corresponding to each version of the package. + */ pkgRecords *Recs; + + /** \brief The md5sum of this package. */ string MD5; + + /** \brief A location in which the actual filename of the package + * should be stored. + */ string &StoreFilename; + + /** \brief The next file for this version to try to download. */ pkgCache::VerFileIterator Vf; + + /** \brief How many (more) times to try to find a new source from + * which to download this package version if it fails. + * + * Set from Acquire::Retries. + */ unsigned int Retries; + + /** \brief \b true if this version file is being downloaded from a + * trusted source. + */ bool Trusted; - // Queue the next available file for download. + /** \brief Queue up the next available file for this version. */ bool QueueNext(); public: - // Specialized action members virtual void Failed(string Message,pkgAcquire::MethodConfig *Cnf); virtual void Done(string Message,unsigned long Size,string Md5Hash, pkgAcquire::MethodConfig *Cnf); @@ -201,18 +811,49 @@ class pkgAcqArchive : public pkgAcquire::Item virtual string DescURI() {return Desc.URI;}; virtual string ShortDesc() {return Desc.ShortDesc;}; virtual void Finished(); + virtual bool IsTrusted(); + /** \brief Create a new pkgAcqArchive. + * + * \param Owner The pkgAcquire object with which this item is + * associated. + * + * \param Sources The sources from which to download version + * files. + * + * \param Recs A package records object, used to look up the file + * corresponding to each version of the package. + * + * \param Version The package version to download. + * + * \param StoreFilename A location in which the actual filename of + * the package should be stored. It will be set to a guessed + * basename in the constructor, and filled in with a fully + * qualified filename once the download finishes. + */ pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources, pkgRecords *Recs,pkgCache::VerIterator const &Version, string &StoreFilename); }; -// Fetch a generic file to the current directory +/** \brief Retrieve an arbitrary file to the current directory. + * + * The file is retrieved even if it is accessed via a URL type that + * normally is a NOP, such as "file". If the download fails, the + * partial file is renamed to get a ".FAILED" extension. + */ class pkgAcqFile : public pkgAcquire::Item { + /** \brief The currently active download process. */ pkgAcquire::ItemDesc Desc; + + /** \brief The md5sum of the file to download, if it is known. */ string Md5Hash; + + /** \brief How many times to retry the download, set from + * Acquire::Retries. + */ unsigned int Retries; public: @@ -224,13 +865,40 @@ class pkgAcqFile : public pkgAcquire::Item virtual string MD5Sum() {return Md5Hash;}; virtual string DescURI() {return Desc.URI;}; - // If DestFilename is empty, download to DestDir/<basename> if - // DestDir is non-empty, $CWD/<basename> otherwise. If - // DestFilename is NOT empty, DestDir is ignored and DestFilename - // is the absolute name to which the file should be downloaded. + /** \brief Create a new pkgAcqFile object. + * + * \param Owner The pkgAcquire object with which this object is + * associated. + * + * \param URI The URI to download. + * + * \param MD5 The md5sum of the file to download, if it is known; + * otherwise "". + * + * \param Size The size of the file to download, if it is known; + * otherwise 0. + * + * \param Desc A description of the file being downloaded. + * + * \param ShortDesc A brief description of the file being + * downloaded. + * + * \param DestDir The directory the file should be downloaded into. + * + * \param DestFilename The filename+path the file is downloaded to. + * + * + * If DestFilename is empty, download to DestDir/<basename> if + * DestDir is non-empty, $CWD/<basename> otherwise. If + * DestFilename is NOT empty, DestDir is ignored and DestFilename + * is the absolute name to which the file should be downloaded. + */ + pkgAcqFile(pkgAcquire *Owner, string URI, string MD5, unsigned long Size, string Desc, string ShortDesc, const string &DestDir="", const string &DestFilename=""); }; +/** @} */ + #endif diff --git a/apt-pkg/acquire-method.h b/apt-pkg/acquire-method.h index 1c0b9f67c..0fd846f54 100644 --- a/apt-pkg/acquire-method.h +++ b/apt-pkg/acquire-method.h @@ -10,6 +10,13 @@ ##################################################################### */ /*}}}*/ + +/** \addtogroup acquire + * @{ + * + * \file acquire-method.h + */ + #ifndef PKGLIB_ACQUIRE_METHOD_H #define PKGLIB_ACQUIRE_METHOD_H @@ -83,4 +90,6 @@ class pkgAcqMethod virtual ~pkgAcqMethod() {}; }; +/** @} */ + #endif diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc index 063aa5ed7..25d40ef54 100644 --- a/apt-pkg/acquire-worker.cc +++ b/apt-pkg/acquire-worker.cc @@ -304,6 +304,13 @@ bool pkgAcquire::Worker::RunMessages() pkgAcquire::Item *Owner = Itm->Owner; pkgAcquire::ItemDesc Desc = *Itm; OwnerQ->ItemDone(Itm); + + // set some status + if(LookupTag(Message,"FailReason") == "Timeout" || + LookupTag(Message,"FailReason") == "TmpResolveFailure" || + LookupTag(Message,"FailReason") == "ConnectionRefused") + Owner->Status = pkgAcquire::Item::StatTransientNetworkError; + Owner->Failed(Message,Config); ItemDone(); diff --git a/apt-pkg/acquire-worker.h b/apt-pkg/acquire-worker.h index d5f8c63d2..2942df69f 100644 --- a/apt-pkg/acquire-worker.h +++ b/apt-pkg/acquire-worker.h @@ -9,13 +9,38 @@ ##################################################################### */ /*}}}*/ + +/** \addtogroup acquire + * @{ + * + * \file acquire-worker.h + */ + #ifndef PKGLIB_ACQUIRE_WORKER_H #define PKGLIB_ACQUIRE_WORKER_H #include <apt-pkg/acquire.h> -// Interfacing to the method process +/** \brief A fetch subprocess. + * + * A worker process is responsible for one stage of the fetch. This + * class encapsulates the communications protocol between the master + * process and the worker, from the master end. + * + * Each worker is intrinsically placed on two linked lists. The + * Queue list (maintained in the #NextQueue variable) is maintained + * by the pkgAcquire::Queue class; it represents the set of workers + * assigned to a particular queue. The Acquire list (maintained in + * the #NextAcquire variable) is maintained by the pkgAcquire class; + * it represents the set of active workers for a particular + * pkgAcquire object. + * + * \todo Like everything else in the Acquire system, this has way too + * many protected items. + * + * \sa pkgAcqMethod, pkgAcquire::Item, pkgAcquire + */ class pkgAcquire::Worker { friend class pkgAcquire; @@ -23,64 +48,274 @@ class pkgAcquire::Worker protected: friend class Queue; - /* Linked list starting at a Queue and a linked list starting - at Acquire */ + /** \brief The next link on the Queue list. + * + * \todo This is always NULL; is it just for future use? + */ Worker *NextQueue; + + /** \brief The next link on the Acquire list. */ Worker *NextAcquire; - // The access association + /** \brief The Queue with which this worker is associated. */ Queue *OwnerQ; + + /** \brief The download progress indicator to which progress + * messages should be sent. + */ pkgAcquireStatus *Log; + + /** \brief The configuration of this method. On startup, the + * target of this pointer is filled in with basic data about the + * method, as reported by the worker. + */ MethodConfig *Config; + + /** \brief The access method to be used by this worker. + * + * \todo Doesn't this duplicate Config->Access? + */ string Access; - // This is the subprocess IPC setup + /** \brief The PID of the subprocess. */ pid_t Process; + + /** \brief A file descriptor connected to the standard output of + * the subprocess. + * + * Used to read messages and data from the subprocess. + */ int InFd; + + /** \brief A file descriptor connected to the standard input of the + * subprocess. + * + * Used to send commands and configuration data to the subprocess. + */ int OutFd; + + /** \brief Set to \b true if the worker is in a state in which it + * might generate data or command responses. + * + * \todo Is this right? It's a guess. + */ bool InReady; + + /** \brief Set to \b true if the worker is in a state in which it + * is legal to send commands to it. + * + * \todo Is this right? + */ bool OutReady; - // Various internal things + /** If \b true, debugging output will be sent to std::clog. */ bool Debug; + + /** \brief The raw text values of messages received from the + * worker, in sequence. + */ vector<string> MessageQueue; + + /** \brief Buffers pending writes to the subprocess. + * + * \todo Wouldn't a std::dequeue be more appropriate? + */ string OutQueue; - // Private constructor helper + /** \brief Common code for the constructor. + * + * Initializes NextQueue and NextAcquire to NULL; Process, InFd, + * and OutFd to -1, OutReady and InReady to \b false, and Debug + * from _config. + */ void Construct(); - // Message handling things + /** \brief Retrieve any available messages from the subprocess. + * + * The messages are retrieved as in ::ReadMessages(), and + * MessageFailure() is invoked if an error occurs; in particular, + * if the pipe to the subprocess dies unexpectedly while a message + * is being read. + * + * \return \b true if the messages were successfully read, \b + * false otherwise. + */ bool ReadMessages(); + + /** \brief Parse and dispatch pending messages. + * + * This dispatches the message in a manner appropriate for its + * type. + * + * \todo Several message types lack separate handlers. + * + * \sa Capabilities(), SendConfiguration(), MediaChange() + */ bool RunMessages(); + + /** \brief Read and dispatch any pending messages from the + * subprocess. + * + * \return \b false if the subprocess died unexpectedly while a + * message was being transmitted. + */ bool InFdReady(); + + /** \brief Send any pending commands to the subprocess. + * + * This method will fail if there is no pending output. + * + * \return \b true if all commands were succeeded, \b false if an + * error occurred (in which case MethodFailure() will be invoked). + */ bool OutFdReady(); - // The message handlers + /** \brief Handle a 100 Capabilities response from the subprocess. + * + * \param Message the raw text of the message from the subprocess. + * + * The message will be parsed and its contents used to fill + * #Config. If #Config is NULL, this routine is a NOP. + * + * \return \b true. + */ bool Capabilities(string Message); + + /** \brief Send a 601 Configuration message (containing the APT + * configuration) to the subprocess. + * + * The APT configuration will be send to the subprocess in a + * message of the following form: + * + * <pre> + * 601 Configuration + * Config-Item: Fully-Qualified-Item=Val + * Config-Item: Fully-Qualified-Item=Val + * ... + * </pre> + * + * \return \b true if the command was successfully sent, \b false + * otherwise. + */ bool SendConfiguration(); + + /** \brief Handle a 403 Media Change message. + * + * \param Message the raw text of the message; the Media field + * indicates what type of media should be changed, and the Drive + * field indicates where the media is located. + * + * Invokes pkgAcquireStatus::MediaChange(Media, Drive) to ask the + * user to swap disks; informs the subprocess of the result (via + * 603 Media Changed, with the Failed field set to \b true if the + * user cancelled the media change). + */ bool MediaChange(string Message); + /** \brief Invoked when the worked process dies unexpectedly. + * + * Waits for the subprocess to terminate and generates an error if + * it terminated abnormally, then closes and blanks out all file + * descriptors. Discards all pending messages from the + * subprocess. + * + * \return \b false. + */ bool MethodFailure(); + + /** \brief Invoked when a fetch job is completed, either + * successfully or unsuccessfully. + * + * Resets the status information for the worker process. + */ void ItemDone(); public: - // The curent method state + /** \brief The queue entry that is currently being downloaded. */ pkgAcquire::Queue::QItem *CurrentItem; + + /** \brief The most recent status string received from the + * subprocess. + */ string Status; + + /** \brief How many bytes of the file have been downloaded. Zero + * if the current progress of the file cannot be determined. + */ unsigned long CurrentSize; + + /** \brief The total number of bytes to be downloaded. Zero if the + * total size of the final is unknown. + */ unsigned long TotalSize; + + /** \brief How much of the file was already downloaded prior to + * starting this worker. + */ unsigned long ResumePoint; - // Load the method and do the startup + /** \brief Tell the subprocess to download the given item. + * + * \param Item the item to queue up. + * \return \b true if the item was successfully enqueued. + * + * Queues up a 600 URI Acquire message for the given item to be + * sent at the next possible moment. Does \e not flush the output + * queue. + */ bool QueueItem(pkgAcquire::Queue::QItem *Item); + + /** \brief Start up the worker and fill in #Config. + * + * Reads the first message from the worker, which is assumed to be + * a 100 Capabilities message. + * + * \return \b true if all operations completed successfully. + */ bool Start(); + + /** \brief Update the worker statistics (CurrentSize, TotalSize, + * etc). + */ void Pulse(); + + /** \return The fetch method configuration. */ inline const MethodConfig *GetConf() const {return Config;}; - + + /** \brief Create a new Worker to download files. + * + * \param OwnerQ The queue into which this worker should be + * placed. + * + * \param Config A location in which to store information about + * the fetch method. + * + * \param Log The download progress indicator that should be used + * to report the progress of this worker. + */ Worker(Queue *OwnerQ,MethodConfig *Config,pkgAcquireStatus *Log); + + /** \brief Create a new Worker that should just retrieve + * information about the fetch method. + * + * Nothing in particular forces you to refrain from actually + * downloading stuff, but the various status callbacks won't be + * invoked. + * + * \param Config A location in which to store information about + * the fetch method. + */ Worker(MethodConfig *Config); + + /** \brief Clean up this worker. + * + * Closes the file descriptors; if MethodConfig::NeedsCleanup is + * \b false, also rudely interrupts the worker with a SIGINT. + */ ~Worker(); }; +/** @} */ + #endif diff --git a/apt-pkg/acquire.cc b/apt-pkg/acquire.cc index ed78ec971..cbd67055d 100644 --- a/apt-pkg/acquire.cc +++ b/apt-pkg/acquire.cc @@ -190,9 +190,9 @@ void pkgAcquire::Enqueue(ItemDesc &Item) Item.Owner->Status = Item::StatIdle; // Queue it into the named queue - I->Enqueue(Item); - ToFetch++; - + if(I->Enqueue(Item)) + ToFetch++; + // Some trace stuff if (Debug == true) { @@ -546,11 +546,17 @@ pkgAcquire::Queue::~Queue() // Queue::Enqueue - Queue an item to the queue /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgAcquire::Queue::Enqueue(ItemDesc &Item) +bool pkgAcquire::Queue::Enqueue(ItemDesc &Item) { QItem **I = &Items; - for (; *I != 0; I = &(*I)->Next); - + // move to the end of the queue and check for duplicates here + for (; *I != 0; I = &(*I)->Next) + if (Item.URI == (*I)->URI) + { + Item.Owner->Status = Item::StatDone; + return false; + } + // Create a new item QItem *Itm = new QItem; *Itm = Item; @@ -560,6 +566,7 @@ void pkgAcquire::Queue::Enqueue(ItemDesc &Item) Item.Owner->QueueCounter++; if (Items->Next == 0) Cycle(); + return true; } /*}}}*/ // Queue::Dequeue - Remove an item from the queue /*{{{*/ diff --git a/apt-pkg/acquire.h b/apt-pkg/acquire.h index 6e836ac49..1de6f5e44 100644 --- a/apt-pkg/acquire.h +++ b/apt-pkg/acquire.h @@ -29,6 +29,40 @@ ##################################################################### */ /*}}}*/ + +/** \defgroup acquire Acquire system + * + * \brief The Acquire system is responsible for retrieving files from + * local or remote URIs and postprocessing them (for instance, + * verifying their authenticity). The core class in this system is + * pkgAcquire, which is responsible for managing the download queues + * during the download. There is at least one download queue for + * each supported protocol; protocols such as http may provide one + * queue per host. + * + * Each file to download is represented by a subclass of + * pkgAcquire::Item. The files add themselves to the download + * queue(s) by providing their URI information to + * pkgAcquire::Item::QueueURI, which calls pkgAcquire::Enqueue. + * + * Once the system is set up, the Run method will spawn subprocesses + * to handle the enqueued URIs; the scheduler will then take items + * from the queues and feed them into the handlers until the queues + * are empty. + * + * \todo Acquire supports inserting an object into several queues at + * once, but it is not clear what its behavior in this case is, and + * no subclass of pkgAcquire::Item seems to actually use this + * capability. + */ + +/** \addtogroup acquire + * + * @{ + * + * \file acquire.h + */ + #ifndef PKGLIB_ACQUIRE_H #define PKGLIB_ACQUIRE_H @@ -43,6 +77,15 @@ using std::string; #include <unistd.h> class pkgAcquireStatus; + +/** \brief The core download scheduler. + * + * This class represents an ongoing download. It manages the lists + * of active and pending downloads and handles setting up and tearing + * down download-related structures. + * + * \todo Why all the protected data items and methods? + */ class pkgAcquire { public: @@ -57,97 +100,299 @@ class pkgAcquire typedef vector<Item *>::iterator ItemIterator; typedef vector<Item *>::const_iterator ItemCIterator; - + protected: - // List of items to fetch + /** \brief A list of items to download. + * + * This is built monotonically as items are created and only + * emptied when the download shuts down. + */ vector<Item *> Items; - // List of active queues and fetched method configuration parameters + /** \brief The head of the list of active queues. + * + * \todo why a hand-managed list of queues instead of std::list or + * std::set? + */ Queue *Queues; + + /** \brief The head of the list of active workers. + * + * \todo why a hand-managed list of workers instead of std::list + * or std::set? + */ Worker *Workers; + + /** \brief The head of the list of acquire method configurations. + * + * Each protocol (http, ftp, gzip, etc) via which files can be + * fetched can have a representation in this list. The + * configuration data is filled in by parsing the 100 Capabilities + * string output by a method on startup (see + * pkgAcqMethod::pkgAcqMethod and pkgAcquire::GetConfig). + * + * \todo why a hand-managed config dictionary instead of std::map? + */ MethodConfig *Configs; + + /** \brief The progress indicator for this download. */ pkgAcquireStatus *Log; + + /** \brief The total size of the files which are to be fetched. + * + * This is not necessarily the total number of bytes to download + * when, e.g., download resumption and list updates via patches + * are taken into account. + */ unsigned long ToFetch; - // Configurable parameters for the schedular - enum {QueueHost,QueueAccess} QueueMode; + // Configurable parameters for the scheduler + + /** \brief Represents the queuing strategy for remote URIs. */ + enum QueueStrategy { + /** \brief Generate one queue for each protocol/host combination; downloads from + * multiple hosts can proceed in parallel. + */ + QueueHost, + /** \brief Generate a single queue for each protocol; serialize + * downloads from multiple hosts. + */ + QueueAccess} QueueMode; + + /** \brief If \b true, debugging information will be dumped to std::clog. */ bool Debug; + /** \brief If \b true, a download is currently in progress. */ bool Running; - + + /** \brief Add the given item to the list of items. */ void Add(Item *Item); + + /** \brief Remove the given item from the list of items. */ void Remove(Item *Item); + + /** \brief Add the given worker to the list of workers. */ void Add(Worker *Work); + + /** \brief Remove the given worker from the list of workers. */ void Remove(Worker *Work); + /** \brief Insert the given fetch request into the appropriate queue. + * + * \param Item The URI to download and the item to download it + * for. Copied by value into the queue; no reference to Item is + * retained. + */ void Enqueue(ItemDesc &Item); + + /** \brief Remove all fetch requests for this item from all queues. */ void Dequeue(Item *Item); + + /** \brief Determine the fetch method and queue of a URI. + * + * \param URI The URI to fetch. + * + * \param[out] Config A location in which to place the method via + * which the URI is to be fetched. + * + * \return the string-name of the queue in which a fetch request + * for the given URI should be placed. + */ string QueueName(string URI,MethodConfig const *&Config); - // FDSET managers for derived classes + /** \brief Build up the set of file descriptors upon which select() should + * block. + * + * The default implementation inserts the file descriptors + * corresponding to active downloads. + * + * \param[out] Fd The largest file descriptor in the generated sets. + * + * \param[out] RSet The set of file descriptors that should be + * watched for input. + * + * \param[out] WSet The set of file descriptors that should be + * watched for output. + */ virtual void SetFds(int &Fd,fd_set *RSet,fd_set *WSet); + + /** Handle input from and output to file descriptors which select() + * has determined are ready. The default implementation + * dispatches to all active downloads. + * + * \param RSet The set of file descriptors that are ready for + * input. + * + * \param WSet The set of file descriptors that are ready for + * output. + */ virtual void RunFds(fd_set *RSet,fd_set *WSet); - // A queue calls this when it dequeues an item + /** \brief Check for idle queues with ready-to-fetch items. + * + * Called by pkgAcquire::Queue::Done each time an item is dequeued + * but remains on some queues; i.e., another queue should start + * fetching it. + */ void Bump(); public: + /** \brief Retrieve information about a fetch method by name. + * + * \param Access The name of the method to look up. + * + * \return the method whose name is Access, or \b NULL if no such method exists. + */ MethodConfig *GetConfig(string Access); - enum RunResult {Continue,Failed,Cancelled}; + /** \brief Provides information on how a download terminated. */ + enum RunResult { + /** \brief All files were fetched successfully. */ + Continue, + + /** \brief Some files failed to download. */ + Failed, + + /** \brief The download was cancelled by the user (i.e., #Log's + * pkgAcquireStatus::Pulse() method returned \b false). + */ + Cancelled}; + + /** \brief Download all the items that have been Add()ed to this + * download process. + * + * This method will block until the download completes, invoking + * methods on #Log to report on the progress of the download. + * + * \param PulseInterval The method pkgAcquireStatus::Pulse will be + * invoked on #Log at intervals of PulseInterval milliseconds. + * + * \return the result of the download. + */ + RunResult Run(int PulseInterval=500000); - RunResult Run(int PulseIntervall=500000); + /** \brief Remove all items from this download process, terminate + * all download workers, and empty all queues. + */ void Shutdown(); - // Simple iteration mechanism + /** \brief Get the first #Worker object. + * + * \return the first active worker in this download process. + */ inline Worker *WorkersBegin() {return Workers;}; + + /** \brief Advance to the next #Worker object. + * + * \return the worker immediately following I, or \b NULL if none + * exists. + */ Worker *WorkerStep(Worker *I); + + /** \brief Get the head of the list of items. */ inline ItemIterator ItemsBegin() {return Items.begin();}; + + /** \brief Get the end iterator of the list of items. */ inline ItemIterator ItemsEnd() {return Items.end();}; // Iterate over queued Item URIs class UriIterator; + /** \brief Get the head of the list of enqueued item URIs. + * + * This iterator will step over every element of every active + * queue. + */ UriIterator UriBegin(); + /** \brief Get the end iterator of the list of enqueued item URIs. */ UriIterator UriEnd(); - // Cleans out the download dir + /** Deletes each entry in the given directory that is not being + * downloaded by this object. For instance, when downloading new + * list files, calling Clean() will delete the old ones. + * + * \param Dir The directory to be cleaned out. + * + * \return \b true if the directory exists and is readable. + */ bool Clean(string Dir); - // Returns the size of the total download set + /** \return the total size in bytes of all the items included in + * this download. + */ double TotalNeeded(); + + /** \return the size in bytes of all non-local items included in + * this download. + */ double FetchNeeded(); + + /** \return the amount of data to be fetched that is already + * present on the filesystem. + */ double PartialPresent(); + /** \brief Construct a new pkgAcquire. + * + * \param Log The progress indicator associated with this + * download, or \b NULL for none. This object is not owned by the + * download process and will not be deleted when the pkgAcquire + * object is destroyed. Naturally, it should live for at least as + * long as the pkgAcquire object does. + */ pkgAcquire(pkgAcquireStatus *Log = 0); + + /** \brief Destroy this pkgAcquire object. + * + * Destroys all queue, method, and item objects associated with + * this download. + */ virtual ~pkgAcquire(); }; -// Description of an Item+URI +/** \brief Represents a single download source from which an item + * should be downloaded. + * + * An item may have several assocated ItemDescs over its lifetime. + */ struct pkgAcquire::ItemDesc { + /** \brief The URI from which to download this item. */ string URI; + /** brief A description of this item. */ string Description; + /** brief A shorter description of this item. */ string ShortDesc; + /** brief The underlying item which is to be downloaded. */ Item *Owner; }; -// List of possible items queued for download. +/** \brief A single download queue in a pkgAcquire object. + * + * \todo Why so many protected values? + */ class pkgAcquire::Queue { friend class pkgAcquire; friend class pkgAcquire::UriIterator; friend class pkgAcquire::Worker; + + /** \brief The next queue in the pkgAcquire object's list of queues. */ Queue *Next; protected: - // Queued item + /** \brief A single item placed in this queue. */ struct QItem : pkgAcquire::ItemDesc { - QItem *Next; + /** \brief The next item in the queue. */ + QItem *Next; + /** \brief The worker associated with this item, if any. */ pkgAcquire::Worker *Worker; - + + /** \brief Assign the ItemDesc portion of this QItem from + * another ItemDesc + */ void operator =(pkgAcquire::ItemDesc const &I) { URI = I.URI; @@ -157,45 +402,145 @@ class pkgAcquire::Queue }; }; - // Name of the queue + /** \brief The name of this queue. */ string Name; - // Items queued into this queue + /** \brief The head of the list of items contained in this queue. + * + * \todo why a by-hand list instead of an STL structure? + */ QItem *Items; + + /** \brief The head of the list of workers associated with this queue. + * + * \todo This is plural because support exists in Queue for + * multiple workers. However, it does not appear that there is + * any way to actually associate more than one worker with a + * queue. + * + * \todo Why not just use a std::set? + */ pkgAcquire::Worker *Workers; + + /** \brief the download scheduler with which this queue is associated. */ pkgAcquire *Owner; + + /** \brief The number of entries in this queue that are currently + * being downloaded. + */ signed long PipeDepth; + + /** \brief The maximum number of entries that this queue will + * attempt to download at once. + */ unsigned long MaxPipeDepth; public: - // Put an item into this queue - void Enqueue(ItemDesc &Item); + /** \brief Insert the given fetch request into this queue. + * + * \return \b true if the queuing was successful. May return + * \b false if the Item is already in the queue + */ + bool Enqueue(ItemDesc &Item); + + /** \brief Remove all fetch requests for the given item from this queue. + * + * \return \b true if at least one request was removed from the queue. + */ bool Dequeue(Item *Owner); - // Find a Queued item + /** \brief Locate an item in this queue. + * + * \param URI A URI to match against. + * \param Owner A pkgAcquire::Worker to match against. + * + * \return the first item in the queue whose URI is #URI and that + * is being downloaded by #Owner. + */ QItem *FindItem(string URI,pkgAcquire::Worker *Owner); + + /** Presumably this should start downloading an item? + * + * \todo Unimplemented. Implement it or remove? + */ bool ItemStart(QItem *Itm,unsigned long Size); + + /** \brief Remove the given item from this queue and set its state + * to pkgAcquire::Item::StatDone. + * + * If this is the only queue containing the item, the item is also + * removed from the main queue by calling pkgAcquire::Dequeue. + * + * \param Itm The item to remove. + * + * \return \b true if no errors are encountered. + */ bool ItemDone(QItem *Itm); + /** \brief Start the worker process associated with this queue. + * + * If a worker process is already associated with this queue, + * this is equivalent to calling Cycle(). + * + * \return \b true if the startup was successful. + */ bool Startup(); + + /** \brief Shut down the worker process associated with this queue. + * + * \param Final If \b true, then the process is stopped unconditionally. + * Otherwise, it is only stopped if it does not need cleanup + * as indicated by the pkgAcqMethod::NeedsCleanup member of + * its configuration. + * + * \return \b true. + */ bool Shutdown(bool Final); + + /** \brief Send idle items to the worker process. + * + * Fills up the pipeline by inserting idle items into the worker's queue. + */ bool Cycle(); + + /** \brief Check for items that could be enqueued. + * + * Call this after an item placed in multiple queues has gone from + * the pkgAcquire::Item::StatFetching state to the + * pkgAcquire::Item::StatIdle state, to possibly refill an empty queue. + * This is an alias for Cycle(). + * + * \todo Why both this and Cycle()? Are they expected to be + * different someday? + */ void Bump(); + /** \brief Create a new Queue. + * + * \param Name The name of the new queue. + * \param Owner The download process that owns the new queue. + */ Queue(string Name,pkgAcquire *Owner); + + /** Shut down all the worker processes associated with this queue + * and empty the queue. + */ ~Queue(); }; +/** \brief Iterates over all the URIs being fetched by a pkgAcquire object. */ class pkgAcquire::UriIterator { + /** The next queue to iterate over. */ pkgAcquire::Queue *CurQ; + /** The item that we currently point at. */ pkgAcquire::Queue::QItem *CurItem; public: - // Advance to the next item inline void operator ++() {operator ++();}; + void operator ++(int) { CurItem = CurItem->Next; @@ -206,11 +551,14 @@ class pkgAcquire::UriIterator } }; - // Accessors inline pkgAcquire::ItemDesc const *operator ->() const {return CurItem;}; inline bool operator !=(UriIterator const &rhs) const {return rhs.CurQ != CurQ || rhs.CurItem != CurItem;}; inline bool operator ==(UriIterator const &rhs) const {return rhs.CurQ == CurQ && rhs.CurItem == CurItem;}; + /** \brief Create a new UriIterator. + * + * \param Q The queue over which this UriIterator should iterate. + */ UriIterator(pkgAcquire::Queue *Q) : CurQ(Q), CurItem(0) { while (CurItem == 0 && CurQ != 0) @@ -221,61 +569,200 @@ class pkgAcquire::UriIterator } }; -// Configuration information from each method +/** \brief Information about the properties of a single acquire method. */ struct pkgAcquire::MethodConfig { + /** \brief The next link on the acquire method list. + * + * \todo Why not an STL container? + */ MethodConfig *Next; + /** \brief The name of this acquire method (e.g., http). */ string Access; + /** \brief The implementation version of this acquire method. */ string Version; + + /** \brief If \b true, only one download queue should be created for this + * method. + */ bool SingleInstance; + + /** \brief If \b true, this method supports pipelined downloading. */ bool Pipeline; + + /** \brief If \b true, the worker process should send the entire + * APT configuration tree to the fetch subprocess when it starts + * up. + */ bool SendConfig; + + /** \brief If \b true, this fetch method does not require network access; + * all files are to be acquired from the local disk. + */ bool LocalOnly; + + /** \brief If \b true, the subprocess has to carry out some cleanup + * actions before shutting down. + * + * For instance, the cdrom method needs to unmount the CD after it + * finishes. + */ bool NeedsCleanup; + + /** \brief If \b true, this fetch method acquires files from removable media. */ bool Removable; + /** \brief Set up the default method parameters. + * + * All fields are initialized to NULL, "", or \b false as + * appropriate. + */ MethodConfig(); }; +/** \brief A monitor object for downloads controlled by the pkgAcquire class. + * + * \todo Why protected members? + * + * \todo Should the double members be uint64_t? + */ class pkgAcquireStatus { protected: + /** \brief The last time at which this monitor object was updated. */ struct timeval Time; + + /** \brief The time at which the download started. */ struct timeval StartTime; + + /** \brief The number of bytes fetched as of the previous call to + * pkgAcquireStatus::Pulse, including local items. + */ double LastBytes; + + /** \brief The current rate of download as of the most recent call + * to pkgAcquireStatus::Pulse, in bytes per second. + */ double CurrentCPS; + + /** \brief The number of bytes fetched as of the most recent call + * to pkgAcquireStatus::Pulse, including local items. + */ double CurrentBytes; + + /** \brief The total number of bytes that need to be fetched. + * + * \warning This member is inaccurate, as new items might be + * enqueued while the download is in progress! + */ double TotalBytes; + + /** \brief The total number of bytes accounted for by items that + * were successfully fetched. + */ double FetchedBytes; + + /** \brief The amount of time that has elapsed since the download + * started. + */ unsigned long ElapsedTime; + + /** \brief The total number of items that need to be fetched. + * + * \warning This member is inaccurate, as new items might be + * enqueued while the download is in progress! + */ unsigned long TotalItems; + + /** \brief The number of items that have been successfully downloaded. */ unsigned long CurrentItems; public: + /** \brief If \b true, the download scheduler should call Pulse() + * at the next available opportunity. + */ bool Update; + + /** \brief If \b true, extra Pulse() invocations will be performed. + * + * With this option set, Pulse() will be called every time that a + * download item starts downloading, finishes downloading, or + * terminates with an error. + */ bool MorePulses; - // Called by items when they have finished a real download + /** \brief Invoked when a local or remote file has been completely fetched. + * + * \param Size The size of the file fetched. + * + * \param ResumePoint How much of the file was already fetched. + */ virtual void Fetched(unsigned long Size,unsigned long ResumePoint); - // Called to change media + /** \brief Invoked when the user should be prompted to change the + * inserted removable media. + * + * This method should not return until the user has confirmed to + * the user interface that the media change is complete. + * + * \param Media The name of the media type that should be changed. + * + * \param Drive The identifying name of the drive whose media + * should be changed. + * + * \return \b true if the user confirms the media change, \b + * false if it is cancelled. + * + * \todo This is a horrible blocking monster; it should be CPSed + * with prejudice. + */ virtual bool MediaChange(string Media,string Drive) = 0; - // Each of these is called by the workers when an event occures + /** \brief Invoked when an item is confirmed to be up-to-date. + + * For instance, when an HTTP download is informed that the file on + * the server was not modified. + */ virtual void IMSHit(pkgAcquire::ItemDesc &/*Itm*/) {}; + + /** \brief Invoked when some of an item's data is fetched. */ virtual void Fetch(pkgAcquire::ItemDesc &/*Itm*/) {}; + + /** \brief Invoked when an item is successfully and completely fetched. */ virtual void Done(pkgAcquire::ItemDesc &/*Itm*/) {}; + + /** \brief Invoked when the process of fetching an item encounters + * a fatal error. + */ virtual void Fail(pkgAcquire::ItemDesc &/*Itm*/) {}; - virtual bool Pulse(pkgAcquire *Owner); // returns false on user cancel + + /** \brief Periodically invoked while the Acquire process is underway. + * + * Subclasses should first call pkgAcquireStatus::Pulse(), then + * update their status output. The download process is blocked + * while Pulse() is being called. + * + * \return \b false if the user asked to cancel the whole Acquire process. + * + * \see pkgAcquire::Run + */ + virtual bool Pulse(pkgAcquire *Owner); + + /** \brief Invoked when the Acquire process starts running. */ virtual void Start(); + + /** \brief Invoked when the Acquire process stops running. */ virtual void Stop(); + /** \brief Initialize all counters to 0 and the time to the current time. */ pkgAcquireStatus(); virtual ~pkgAcquireStatus() {}; }; +/** @} */ + #endif diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc index 3ac98216b..3f018917c 100644 --- a/apt-pkg/algorithms.cc +++ b/apt-pkg/algorithms.cc @@ -17,10 +17,13 @@ #include <apt-pkg/algorithms.h> #include <apt-pkg/error.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/version.h> #include <apt-pkg/sptr.h> + #include <apti18n.h> - +#include <cstdlib> +#include <algorithm> #include <iostream> /*}}}*/ using namespace std; @@ -97,6 +100,7 @@ bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/) DepIterator End; D.GlobOr(Start,End); if (Start->Type == pkgCache::Dep::Conflicts || + Start->Type == pkgCache::Dep::DpkgBreaks || Start->Type == pkgCache::Dep::Obsoletes || End->Type == pkgCache::Dep::PreDepends) { @@ -146,6 +150,8 @@ bool pkgSimulate::Configure(PkgIterator iPkg) cout << " Obsoletes:" << D.TargetPkg().Name(); else if (D->Type == pkgCache::Dep::Conflicts) cout << " Conflicts:" << D.TargetPkg().Name(); + else if (D->Type == pkgCache::Dep::DpkgBreaks) + cout << " Breaks:" << D.TargetPkg().Name(); else cout << " Depends:" << D.TargetPkg().Name(); } @@ -217,6 +223,8 @@ void pkgSimulate::ShortBreaks() the necessary calculations to deal with the problems. */ bool pkgApplyStatus(pkgDepCache &Cache) { + pkgDepCache::ActionGroup group(Cache); + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) { if (I->VersionList == 0) @@ -227,13 +235,13 @@ bool pkgApplyStatus(pkgDepCache &Cache) I->InstState == pkgCache::State::HoldReInstReq) { if (I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); else { // Is this right? Will dpkg choke on an upgrade? if (Cache[I].CandidateVer != 0 && Cache[I].CandidateVerIter(Cache).Downloadable() == true) - Cache.MarkInstall(I); + Cache.MarkInstall(I, false, 0, false); else return _error->Error(_("The package %s needs to be reinstalled, " "but I can't find an archive for it."),I.Name()); @@ -250,12 +258,12 @@ bool pkgApplyStatus(pkgDepCache &Cache) case pkgCache::State::HalfConfigured: if ((I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) || I.State() != pkgCache::PkgIterator::NeedsUnpack) - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); else { if (Cache[I].CandidateVer != 0 && Cache[I].CandidateVerIter(Cache).Downloadable() == true) - Cache.MarkInstall(I); + Cache.MarkInstall(I, true, 0, false); else Cache.MarkDelete(I); } @@ -281,10 +289,12 @@ bool pkgApplyStatus(pkgDepCache &Cache) on the result. */ bool pkgFixBroken(pkgDepCache &Cache) { + pkgDepCache::ActionGroup group(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); + Cache.MarkInstall(I, true, 0, false); /* Fix packages that are in a NeedArchive state but don't have a downloadable install version */ @@ -297,7 +307,7 @@ bool pkgFixBroken(pkgDepCache &Cache) if (Cache[I].InstVerIter(Cache).Downloadable() == false) continue; - Cache.MarkInstall(I,true); + Cache.MarkInstall(I, true, 0, false); } pkgProblemResolver Fix(&Cache); @@ -314,23 +324,25 @@ bool pkgFixBroken(pkgDepCache &Cache) */ bool pkgDistUpgrade(pkgDepCache &Cache) { + pkgDepCache::ActionGroup group(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); + Cache.MarkInstall(I, true, 0, false); /* 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 & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) - Cache.MarkInstall(I,true); + Cache.MarkInstall(I, true, 0, false); /* 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); + Cache.MarkInstall(I, false, 0, false); pkgProblemResolver Fix(&Cache); @@ -342,7 +354,7 @@ bool pkgDistUpgrade(pkgDepCache &Cache) if (I->SelectedState == pkgCache::State::Hold) { Fix.Protect(I); - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); } } } @@ -357,6 +369,8 @@ bool pkgDistUpgrade(pkgDepCache &Cache) to install packages not marked for install */ bool pkgAllUpgrade(pkgDepCache &Cache) { + pkgDepCache::ActionGroup group(Cache); + pkgProblemResolver Fix(&Cache); if (Cache.BrokenCount() != 0) @@ -373,7 +387,7 @@ bool pkgAllUpgrade(pkgDepCache &Cache) continue; if (I->CurrentVer != 0 && Cache[I].InstallVer != 0) - Cache.MarkInstall(I,false); + Cache.MarkInstall(I, false, 0, false); } return Fix.ResolveByKeep(); @@ -386,6 +400,8 @@ bool pkgAllUpgrade(pkgDepCache &Cache) the package is restored. */ bool pkgMinimizeUpgrade(pkgDepCache &Cache) { + pkgDepCache::ActionGroup group(Cache); + if (Cache.BrokenCount() != 0) return false; @@ -402,9 +418,9 @@ bool pkgMinimizeUpgrade(pkgDepCache &Cache) continue; // Keep it and see if that is OK - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); if (Cache.BrokenCount() != 0) - Cache.MarkInstall(I,false); + Cache.MarkInstall(I, false, 0, false); else { // If keep didnt actually do anything then there was no change.. @@ -491,8 +507,10 @@ void pkgProblemResolver::MakeScores() 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) + on the same level. We enhance the score of installed packages + if those are not obsolete + */ + if (I->CurrentVer != 0 && Cache[I].CandidateVer != 0 && Cache[I].CandidateVerIter(Cache).Downloadable()) Score += 1; } @@ -562,6 +580,8 @@ void pkgProblemResolver::MakeScores() installable */ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) { + pkgDepCache::ActionGroup group(Cache); + if ((Flags[Pkg->ID] & Upgradable) == 0 || Cache[Pkg].Upgradable() == false) return false; if ((Flags[Pkg->ID] & Protected) == Protected) @@ -570,7 +590,7 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) Flags[Pkg->ID] &= ~Upgradable; bool WasKept = Cache[Pkg].Keep(); - Cache.MarkInstall(Pkg,false); + Cache.MarkInstall(Pkg, false, 0, false); // This must be a virtual package or something like that. if (Cache[Pkg].InstVerIter(Cache).end() == true) @@ -634,6 +654,7 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) /* We let the algorithm deal with conflicts on its next iteration, it is much smarter than us */ if (Start->Type == pkgCache::Dep::Conflicts || + Start->Type == pkgCache::Dep::DpkgBreaks || Start->Type == pkgCache::Dep::Obsoletes) break; @@ -655,7 +676,7 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) if (Fail == true) { if (WasKept == true) - Cache.MarkKeep(Pkg); + Cache.MarkKeep(Pkg, false, false); else Cache.MarkDelete(Pkg); return false; @@ -682,6 +703,8 @@ bool pkgProblemResolver::DoUpgrade(pkgCache::PkgIterator Pkg) upgrade packages to advoid problems. */ bool pkgProblemResolver::Resolve(bool BrokenFix) { + pkgDepCache::ActionGroup group(Cache); + unsigned long Size = Cache.Head().PackageCount; // Record which packages are marked for install @@ -697,7 +720,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) { if (Cache[I].InstBroken() == true && BrokenFix == true) { - Cache.MarkInstall(I,false); + Cache.MarkInstall(I, false, 0, false); if (Cache[I].Install() == true) Again = true; } @@ -763,14 +786,14 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) pkgCache::Version *OldVer = Cache[I].InstallVer; Flags[I->ID] &= ReInstateTried; - Cache.MarkInstall(I,false); + Cache.MarkInstall(I, false, 0, false); if (Cache[I].InstBroken() == true || OldBreaks < Cache.BrokenCount()) { if (OldVer == 0) Cache.MarkDelete(I); else - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); } else if (Debug == true) @@ -781,7 +804,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) continue; if (Debug == true) - cout << "Investigating " << I.Name() << endl; + clog << "Investigating " << I.Name() << endl; // Isolate the problem dependency PackageKill KillList[100]; @@ -815,7 +838,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) { if (Debug == true) clog << " Or group keep for " << I.Name() << endl; - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); Change = true; } } @@ -836,7 +859,12 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) OldEnd = LEnd; } else + { Start++; + // We only worry about critical deps. + if (Start.IsCritical() != true) + continue; + } // Dep is ok if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall) @@ -854,6 +882,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) SPtrArray<pkgCache::Version *> VList = Start.AllTargets(); if (*VList == 0 && (Flags[I->ID] & Protected) != Protected && Start->Type != pkgCache::Dep::Conflicts && + Start->Type != pkgCache::Dep::DpkgBreaks && Start->Type != pkgCache::Dep::Obsoletes && Cache[I].NowBroken() == false) { @@ -865,7 +894,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) } Change = true; - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); break; } @@ -884,6 +913,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) if (Scores[I->ID] <= Scores[Pkg->ID] || ((Cache[Start] & pkgDepCache::DepNow) == 0 && End->Type != pkgCache::Dep::Conflicts && + End->Type != pkgCache::Dep::DpkgBreaks && End->Type != pkgCache::Dep::Obsoletes)) { // Try a little harder to fix protected packages.. @@ -902,7 +932,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) /* See if a keep will do, unless the package is protected, then installing it will be necessary */ bool Installed = Cache[I].Install(); - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); if (Cache[I].InstBroken() == false) { // Unwind operation will be keep now @@ -911,7 +941,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) // Restore if (InOr == true && Installed == true) - Cache.MarkInstall(I,false); + Cache.MarkInstall(I, false, 0, false); if (Debug == true) clog << " Holding Back " << I.Name() << " rather than change " << Start.TargetPkg().Name() << endl; @@ -949,7 +979,22 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) (Start->Type == pkgCache::Dep::Conflicts || Start->Type == pkgCache::Dep::Obsoletes)) continue; - + + if (Start->Type == pkgCache::Dep::DpkgBreaks) + { + /* Would it help if we upgraded? */ + if (Cache[End] & pkgDepCache::DepGCVer) { + if (Debug) + clog << " Upgrading " << Pkg.Name() << " due to Breaks field in " << I.Name() << endl; + Cache.MarkInstall(Pkg, false, 0, false); + continue; + } + if (Debug) + clog << " Will not break " << Pkg.Name() << " as stated in Breaks field in " << I.Name() <<endl; + Cache.MarkKeep(I, false, false); + continue; + } + // Skip adding to the kill list if it is protected if ((Flags[Pkg->ID] & Protected) != 0) continue; @@ -970,6 +1015,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) // Hm, nothing can possibly satisify this dep. Nuke it. if (VList[0] == 0 && Start->Type != pkgCache::Dep::Conflicts && + Start->Type != pkgCache::Dep::DpkgBreaks && Start->Type != pkgCache::Dep::Obsoletes && (Flags[I->ID] & Protected) != Protected) { @@ -983,7 +1029,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) // Restore if (InOr == true && Installed == true) - Cache.MarkInstall(I,false); + Cache.MarkInstall(I, false, 0, false); if (Debug == true) clog << " Holding Back " << I.Name() << " because I can't find " << Start.TargetPkg().Name() << endl; @@ -1028,7 +1074,7 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) { if (Debug == true) clog << " Fixing " << I.Name() << " via keep of " << J->Pkg.Name() << endl; - Cache.MarkKeep(J->Pkg); + Cache.MarkKeep(J->Pkg, false, false); } if (Counter > 1) @@ -1058,6 +1104,20 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) return _error->Error(_("Unable to correct problems, you have held broken packages.")); } + // set the auto-flags (mvo: I'm not sure if we _really_ need this, but + // I didn't managed + pkgCache::PkgIterator I = Cache.PkgBegin(); + for (;I.end() != true; I++) { + if (Cache[I].NewInstall() && !(Flags[I->ID] & PreInstalled)) { + if(_config->FindI("Debug::pkgAutoRemove",false)) { + std::clog << "Resolve installed new pkg: " << I.Name() + << " (now marking it as auto)" << std::endl; + } + Cache[I].Flags |= pkgCache::Flag::Auto; + } + } + + return true; } /*}}}*/ @@ -1068,6 +1128,8 @@ bool pkgProblemResolver::Resolve(bool BrokenFix) system was non-broken previously. */ bool pkgProblemResolver::ResolveByKeep() { + pkgDepCache::ActionGroup group(Cache); + unsigned long Size = Cache.Head().PackageCount; if (Debug == true) @@ -1101,7 +1163,7 @@ bool pkgProblemResolver::ResolveByKeep() { if (Debug == true) clog << "Keeping package " << I.Name() << endl; - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); if (Cache[I].InstBroken() == false) { K = PList - 1; @@ -1149,7 +1211,7 @@ bool pkgProblemResolver::ResolveByKeep() { if (Debug == true) clog << " Keeping Package " << Pkg.Name() << " due to dep" << endl; - Cache.MarkKeep(Pkg); + Cache.MarkKeep(Pkg, false, false); } if (Cache[I].InstBroken() == false) @@ -1186,14 +1248,21 @@ bool pkgProblemResolver::ResolveByKeep() /* This is used to make sure protected packages are installed */ void pkgProblemResolver::InstallProtect() { + pkgDepCache::ActionGroup group(Cache); + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) { if ((Flags[I->ID] & Protected) == Protected) { if ((Flags[I->ID] & ToRemove) == ToRemove) Cache.MarkDelete(I); - else - Cache.MarkInstall(I,false); + else + { + // preserver the information if the package was auto + // or manual installed + bool autoInst = (Cache[I].Flags & pkgCache::Flag::Auto); + Cache.MarkInstall(I, false, 0, !autoInst); + } } } } @@ -1229,3 +1298,4 @@ void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List) qsort(List,Count,sizeof(*List),PrioComp); } /*}}}*/ + diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h index c412eaa97..08eafca0f 100644 --- a/apt-pkg/cacheiterators.h +++ b/apt-pkg/cacheiterators.h @@ -96,7 +96,7 @@ class pkgCache::VerIterator { Version *Ver; pkgCache *Owner; - + void _dummy(); public: @@ -125,6 +125,8 @@ class pkgCache::VerIterator inline const char *Section() const {return Ver->Section == 0?0:Owner->StrP + Ver->Section;}; inline const char *Arch() const {return Ver->Arch == 0?0:Owner->StrP + Ver->Arch;}; inline PkgIterator ParentPkg() const {return PkgIterator(*Owner,Owner->PkgP + Ver->ParentPkg);}; + inline DescIterator DescriptionList() const; + DescIterator TranslatedDescription() const; inline DepIterator DependsList() const; inline PrvIterator ProvidesList() const; inline VerFileIterator FileList() const; @@ -145,6 +147,50 @@ class pkgCache::VerIterator }; }; +// Description Iterator +class pkgCache::DescIterator +{ + Description *Desc; + pkgCache *Owner; + + void _dummy(); + + public: + + // Iteration + void operator ++(int) {if (Desc != Owner->DescP) Desc = Owner->DescP + Desc->NextDesc;}; + inline void operator ++() {operator ++(0);}; + inline bool end() const {return Desc == Owner->DescP?true:false;}; + inline void operator =(const DescIterator &B) {Desc = B.Desc; Owner = B.Owner;}; + + // Comparison + inline bool operator ==(const DescIterator &B) const {return Desc == B.Desc;}; + inline bool operator !=(const DescIterator &B) const {return Desc != B.Desc;}; + int CompareDesc(const DescIterator &B) const; + + // Accessors + inline Description *operator ->() {return Desc;}; + inline Description const *operator ->() const {return Desc;}; + inline Description &operator *() {return *Desc;}; + inline Description const &operator *() const {return *Desc;}; + inline operator Description *() {return Desc == Owner->DescP?0:Desc;}; + inline operator Description const *() const {return Desc == Owner->DescP?0:Desc;}; + inline pkgCache *Cache() {return Owner;}; + + inline const char *LanguageCode() const {return Owner->StrP + Desc->language_code;}; + inline const char *md5() const {return Owner->StrP + Desc->md5sum;}; + inline DescFileIterator FileList() const; + inline unsigned long Index() const {return Desc - Owner->DescP;}; + + inline DescIterator() : Desc(0), Owner(0) {}; + inline DescIterator(pkgCache &Owner,Description *Trg = 0) : Desc(Trg), + Owner(&Owner) + { + if (Desc == 0) + Desc = Owner.DescP; + }; +}; + // Dependency iterator class pkgCache::DepIterator { @@ -335,6 +381,38 @@ class pkgCache::VerFileIterator inline VerFileIterator(pkgCache &Owner,VerFile *Trg) : Owner(&Owner), FileP(Trg) {}; }; +// Description File +class pkgCache::DescFileIterator +{ + pkgCache *Owner; + DescFile *FileP; + + public: + + // Iteration + void operator ++(int) {if (FileP != Owner->DescFileP) FileP = Owner->DescFileP + FileP->NextFile;}; + inline void operator ++() {operator ++(0);}; + inline bool end() const {return FileP == Owner->DescFileP?true:false;}; + + // Comparison + inline bool operator ==(const DescFileIterator &B) const {return FileP == B.FileP;}; + inline bool operator !=(const DescFileIterator &B) const {return FileP != B.FileP;}; + + // Accessors + inline DescFile *operator ->() {return FileP;}; + inline DescFile const *operator ->() const {return FileP;}; + inline DescFile const &operator *() const {return *FileP;}; + inline operator DescFile *() {return FileP == Owner->DescFileP?0:FileP;}; + inline operator DescFile const *() const {return FileP == Owner->DescFileP?0:FileP;}; + inline pkgCache *Cache() {return Owner;}; + + inline PkgFileIterator File() const {return PkgFileIterator(*Owner,FileP->File + Owner->PkgFileP);}; + inline unsigned long Index() const {return FileP - Owner->DescFileP;}; + + inline DescFileIterator() : Owner(0), FileP(0) {}; + inline DescFileIterator(pkgCache &Owner,DescFile *Trg) : Owner(&Owner), FileP(Trg) {}; +}; + // Inlined Begin functions cant be in the class because of order problems inline pkgCache::VerIterator pkgCache::PkgIterator::VersionList() const {return VerIterator(*Owner,Owner->VerP + Pkg->VersionList);}; @@ -344,11 +422,15 @@ inline pkgCache::DepIterator pkgCache::PkgIterator::RevDependsList() const {return DepIterator(*Owner,Owner->DepP + Pkg->RevDepends,Pkg);}; inline pkgCache::PrvIterator pkgCache::PkgIterator::ProvidesList() const {return PrvIterator(*Owner,Owner->ProvideP + Pkg->ProvidesList,Pkg);}; +inline pkgCache::DescIterator pkgCache::VerIterator::DescriptionList() const + {return DescIterator(*Owner,Owner->DescP + Ver->DescriptionList);}; inline pkgCache::PrvIterator pkgCache::VerIterator::ProvidesList() const {return PrvIterator(*Owner,Owner->ProvideP + Ver->ProvidesList,Ver);}; inline pkgCache::DepIterator pkgCache::VerIterator::DependsList() const {return DepIterator(*Owner,Owner->DepP + Ver->DependsList,Ver);}; inline pkgCache::VerFileIterator pkgCache::VerIterator::FileList() const {return VerFileIterator(*Owner,Owner->VerFileP + Ver->FileList);}; +inline pkgCache::DescFileIterator pkgCache::DescIterator::FileList() const + {return DescFileIterator(*Owner,Owner->DescFileP + Desc->FileList);}; #endif diff --git a/apt-pkg/cdrom.cc b/apt-pkg/cdrom.cc index 9b0568d8f..4a688a5e1 100644 --- a/apt-pkg/cdrom.cc +++ b/apt-pkg/cdrom.cc @@ -15,6 +15,7 @@ #include <dirent.h> #include <unistd.h> #include <stdio.h> +#include <algorithm> #include "indexcopy.h" @@ -27,12 +28,16 @@ using namespace std; search that short circuits when it his a package file in the dir. This speeds it up greatly as the majority of the size is in the binary-* sub dirs. */ -bool pkgCdrom::FindPackages(string CD,vector<string> &List, - vector<string> &SList, vector<string> &SigList, +bool pkgCdrom::FindPackages(string CD, + vector<string> &List, + vector<string> &SList, + vector<string> &SigList, + vector<string> &TransList, string &InfoDir, pkgCdromStatus *log, unsigned int Depth) { static ino_t Inodes[9]; + DIR *D; // if we have a look we "pulse" now if(log) @@ -87,8 +92,28 @@ bool pkgCdrom::FindPackages(string CD,vector<string> &List, if (_config->FindB("APT::CDROM::Thorough",false) == false) return true; } + + // see if we find translatin indexes + if (stat("i18n",&Buf) == 0) + { + D = opendir("i18n"); + for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D)) + { + if(strstr(Dir->d_name,"Translation") != NULL) + { + if (_config->FindB("Debug::aptcdrom",false) == true) + std::clog << "found translations: " << Dir->d_name << "\n"; + string file = Dir->d_name; + if(file.substr(file.size()-3,file.size()) == ".gz") + file = file.substr(0,file.size()-3); + TransList.push_back(CD+"i18n/"+ file); + } + } + closedir(D); + } + - DIR *D = opendir("."); + D = opendir("."); if (D == 0) return _error->Errno("opendir","Unable to read %s",CD.c_str()); @@ -124,7 +149,7 @@ bool pkgCdrom::FindPackages(string CD,vector<string> &List, Inodes[Depth] = Buf.st_ino; // Descend - if (FindPackages(CD + Dir->d_name,List,SList,SigList,InfoDir,log,Depth+1) == false) + if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false) break; if (chdir(CD.c_str()) != 0) @@ -609,9 +634,10 @@ bool pkgCdrom::Add(pkgCdromStatus *log) vector<string> List; vector<string> SourceList; vector<string> SigList; + vector<string> TransList; string StartDir = SafeGetCWD(); string InfoDir; - if (FindPackages(CDROM,List,SourceList, SigList,InfoDir,log) == false) + if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false) { log->Update("\n"); return false; @@ -639,17 +665,20 @@ bool pkgCdrom::Add(pkgCdromStatus *log) DropRepeats(List,"Packages"); DropRepeats(SourceList,"Sources"); DropRepeats(SigList,"Release.gpg"); + DropRepeats(TransList,""); if(log) { msg.str(""); - ioprintf(msg, _("Found %i package indexes, %i source indexes and " - "%i signatures\n"), - List.size(), SourceList.size(), SigList.size()); + ioprintf(msg, _("Found %i package indexes, %i source indexes, " + "%i translation indexes and %i signatures\n"), + List.size(), SourceList.size(), TransList.size(), + SigList.size()); log->Update(msg.str(), STEP_SCAN); } if (List.size() == 0 && SourceList.size() == 0) { - UnmountCdrom(CDROM); + if (_config->FindB("APT::CDROM::NoMount",false) == false) + UnmountCdrom(CDROM); return _error->Error("Unable to locate any package files, perhaps this is not a Debian Disc"); } @@ -676,7 +705,7 @@ bool pkgCdrom::Add(pkgCdromStatus *log) if(log) { msg.str(""); - ioprintf(msg, "Found label '%s'\n", Name.c_str()); + ioprintf(msg, _("Found label '%s'\n"), Name.c_str()); log->Update(msg.str()); } Database.Set("CD::" + ID + "::Label",Name); @@ -688,7 +717,8 @@ bool pkgCdrom::Add(pkgCdromStatus *log) { if(!log) { - UnmountCdrom(CDROM); + if (_config->FindB("APT::CDROM::NoMount",false) == false) + UnmountCdrom(CDROM); return _error->Error("No disc name found and no way to ask for it"); } @@ -733,8 +763,10 @@ bool pkgCdrom::Add(pkgCdromStatus *log) // Copy the package files to the state directory PackageCopy Copy; SourceCopy SrcCopy; + TranslationsCopy TransCopy; if (Copy.CopyPackages(CDROM,Name,List, log) == false || - SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false) + SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false || + TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false) return false; // reduce the List so that it takes less space in sources.list @@ -764,7 +796,8 @@ bool pkgCdrom::Add(pkgCdromStatus *log) string::size_type Space = (*I).find(' '); if (Space == string::npos) { - UnmountCdrom(CDROM); + if (_config->FindB("APT::CDROM::NoMount",false) == false) + UnmountCdrom(CDROM); return _error->Error("Internal error"); } @@ -781,7 +814,8 @@ bool pkgCdrom::Add(pkgCdromStatus *log) string::size_type Space = (*I).find(' '); if (Space == string::npos) { - UnmountCdrom(CDROM); + if (_config->FindB("APT::CDROM::NoMount",false) == false) + UnmountCdrom(CDROM); return _error->Error("Internal error"); } @@ -797,7 +831,7 @@ bool pkgCdrom::Add(pkgCdromStatus *log) // Unmount and finish if (_config->FindB("APT::CDROM::NoMount",false) == false) { - log->Update(_("Unmounting CD-ROM..."), STEP_LAST); + log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST); UnmountCdrom(CDROM); } diff --git a/apt-pkg/cdrom.h b/apt-pkg/cdrom.h index b8b208c30..68d61c098 100644 --- a/apt-pkg/cdrom.h +++ b/apt-pkg/cdrom.h @@ -47,8 +47,11 @@ class pkgCdrom }; - bool FindPackages(string CD,vector<string> &List, - vector<string> &SList, vector<string> &SigList, + bool FindPackages(string CD, + vector<string> &List, + vector<string> &SList, + vector<string> &SigList, + vector<string> &TransList, string &InfoDir, pkgCdromStatus *log, unsigned int Depth = 0); bool DropBinaryArch(vector<string> &List); diff --git a/apt-pkg/contrib/cdromutl.h b/apt-pkg/contrib/cdromutl.h index 1264982a8..f24bb8c70 100644 --- a/apt-pkg/contrib/cdromutl.h +++ b/apt-pkg/contrib/cdromutl.h @@ -8,7 +8,7 @@ ##################################################################### */ /*}}}*/ #ifndef PKGLIB_CDROMUTL_H -#define PKGLIB_ACQUIRE_METHOD_H +#define PKGLIB_CDROMUTL_H #include <string> @@ -17,5 +17,6 @@ using std::string; bool MountCdrom(string Path); bool UnmountCdrom(string Path); bool IdentCdrom(string CD,string &Res,unsigned int Version = 2); +bool IsMounted(string &Path); #endif diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index 70ac52e30..d19a92e62 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -21,6 +21,7 @@ #include <apti18n.h> +#include <cstdlib> #include <iostream> #include <unistd.h> #include <fcntl.h> diff --git a/apt-pkg/contrib/progress.cc b/apt-pkg/contrib/progress.cc index ffd1191e5..6ce6e950a 100644 --- a/apt-pkg/contrib/progress.cc +++ b/apt-pkg/contrib/progress.cc @@ -112,6 +112,8 @@ bool OpProgress::CheckChange(float Interval) if ((int)LastPercent == (int)Percent) return false; + + LastPercent = Percent; if (Interval == 0) return false; @@ -123,7 +125,6 @@ bool OpProgress::CheckChange(float Interval) if (Diff < Interval) return false; LastTime = Now; - LastPercent = Percent; return true; } /*}}}*/ diff --git a/apt-pkg/contrib/sha256.cc b/apt-pkg/contrib/sha256.cc index b75ce8a84..ecda3d8e8 100644 --- a/apt-pkg/contrib/sha256.cc +++ b/apt-pkg/contrib/sha256.cc @@ -18,6 +18,12 @@ * any later version. * */ + +#ifdef __GNUG__ +#pragma implementation "apt-pkg/sha256.h" +#endif + + #define SHA256_DIGEST_SIZE 32 #define SHA256_HMAC_BLOCK_SIZE 64 diff --git a/apt-pkg/contrib/sha256.h b/apt-pkg/contrib/sha256.h index 01b638d56..c490bfa4d 100644 --- a/apt-pkg/contrib/sha256.h +++ b/apt-pkg/contrib/sha256.h @@ -16,7 +16,6 @@ #include <string> #include <algorithm> -#include <stdint.h> using std::string; using std::min; diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc index aa37a099a..006452af4 100644 --- a/apt-pkg/contrib/strutl.cc +++ b/apt-pkg/contrib/strutl.cc @@ -24,16 +24,60 @@ #include <ctype.h> #include <string.h> #include <stdio.h> +#include <algorithm> #include <unistd.h> #include <regex.h> #include <errno.h> #include <stdarg.h> +#include <iconv.h> #include "config.h" using namespace std; /*}}}*/ +// UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/ +// --------------------------------------------------------------------- +/* This is handy to use before display some information for enduser */ +bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest) +{ + iconv_t cd; + const char *inbuf; + char *inptr, *outbuf, *outptr; + size_t insize, outsize; + + cd = iconv_open(codeset, "UTF-8"); + if (cd == (iconv_t)(-1)) { + // Something went wrong + if (errno == EINVAL) + _error->Error("conversion from 'UTF-8' to '%s' not available", + codeset); + else + perror("iconv_open"); + + // Clean the destination string + *dest = ""; + + return false; + } + + insize = outsize = orig.size(); + inbuf = orig.data(); + inptr = (char *)inbuf; + outbuf = new char[insize+1]; + outptr = outbuf; + + iconv(cd, &inptr, &insize, &outptr, &outsize); + *outptr = '\0'; + + *dest = outbuf; + delete[] outbuf; + + iconv_close(cd); + + return true; +} + /*}}}*/ // 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 diff --git a/apt-pkg/contrib/strutl.h b/apt-pkg/contrib/strutl.h index b8553ab58..ae1244ecd 100644 --- a/apt-pkg/contrib/strutl.h +++ b/apt-pkg/contrib/strutl.h @@ -36,7 +36,8 @@ using std::ostream; #define APT_FORMAT2 #define APT_FORMAT3 #endif - + +bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest); char *_strstrip(char *String); char *_strtabexpand(char *String,size_t Len); bool ParseQuoteWord(const char *&String,string &Res); diff --git a/apt-pkg/deb/debindexfile.cc b/apt-pkg/deb/debindexfile.cc index 2d52f7c45..ed7633803 100644 --- a/apt-pkg/deb/debindexfile.cc +++ b/apt-pkg/deb/debindexfile.cc @@ -301,7 +301,7 @@ pkgCache::PkgFileIterator debPackagesIndex::FindInCache(pkgCache &Cache) const pkgCache::PkgFileIterator File = Cache.FileBegin(); for (; File.end() == false; File++) { - if (FileName != File.FileName()) + if (File.FileName() == NULL || FileName != File.FileName()) continue; struct stat St; @@ -316,6 +316,170 @@ pkgCache::PkgFileIterator debPackagesIndex::FindInCache(pkgCache &Cache) const } /*}}}*/ +// TranslationsIndex::debTranslationsIndex - Contructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +debTranslationsIndex::debTranslationsIndex(string URI,string Dist,string Section) : + pkgIndexFile(true), URI(URI), Dist(Dist), Section(Section) +{ +} + /*}}}*/ +// TranslationIndex::Trans* - Return the URI to the translation files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +inline string debTranslationsIndex::IndexFile(const char *Type) const +{ + return _config->FindDir("Dir::State::lists") + URItoFileName(IndexURI(Type)); +} +string debTranslationsIndex::IndexURI(const char *Type) const +{ + string Res; + if (Dist[Dist.size() - 1] == '/') + { + if (Dist != "/") + Res = URI + Dist; + else + Res = URI; + } + else + Res = URI + "dists/" + Dist + '/' + Section + + "/i18n/Translation-"; + + Res += Type; + return Res; +} + /*}}}*/ +// TranslationsIndex::GetIndexes - Fetch the index files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debTranslationsIndex::GetIndexes(pkgAcquire *Owner) const +{ + if (TranslationsAvailable()) { + string TranslationFile = "Translation-" + LanguageCode(); + new pkgAcqIndexTrans(Owner, IndexURI(LanguageCode().c_str()), + Info(TranslationFile.c_str()), + TranslationFile); + } + + return true; +} + /*}}}*/ +// TranslationsIndex::Describe - Give a descriptive path to the index /*{{{*/ +// --------------------------------------------------------------------- +/* This should help the user find the index in the sources.list and + in the filesystem for problem solving */ +string debTranslationsIndex::Describe(bool Short) const +{ + char S[300]; + if (Short == true) + snprintf(S,sizeof(S),"%s",Info(TranslationFile().c_str()).c_str()); + else + snprintf(S,sizeof(S),"%s (%s)",Info(TranslationFile().c_str()).c_str(), + IndexFile(LanguageCode().c_str()).c_str()); + return S; +} + /*}}}*/ +// TranslationsIndex::Info - One liner describing the index URI /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string debTranslationsIndex::Info(const char *Type) const +{ + string Info = ::URI::SiteOnly(URI) + ' '; + if (Dist[Dist.size() - 1] == '/') + { + if (Dist != "/") + Info += Dist; + } + else + Info += Dist + '/' + Section; + Info += " "; + Info += Type; + return Info; +} + /*}}}*/ +bool debTranslationsIndex::HasPackages() const +{ + if(!TranslationsAvailable()) + return false; + + return FileExists(IndexFile(LanguageCode().c_str())); +} + +// TranslationsIndex::Exists - Check if the index is available /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debTranslationsIndex::Exists() const +{ + return FileExists(IndexFile(LanguageCode().c_str())); +} + /*}}}*/ +// TranslationsIndex::Size - Return the size of the index /*{{{*/ +// --------------------------------------------------------------------- +/* This is really only used for progress reporting. */ +unsigned long debTranslationsIndex::Size() const +{ + struct stat S; + if (stat(IndexFile(LanguageCode().c_str()).c_str(),&S) != 0) + return 0; + return S.st_size; +} + /*}}}*/ +// TranslationsIndex::Merge - Load the index file into a cache /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debTranslationsIndex::Merge(pkgCacheGenerator &Gen,OpProgress &Prog) const +{ + // Check the translation file, if in use + string TranslationFile = IndexFile(LanguageCode().c_str()); + if (TranslationsAvailable() && FileExists(TranslationFile)) + { + FileFd Trans(TranslationFile,FileFd::ReadOnly); + debListParser TransParser(&Trans); + if (_error->PendingError() == true) + return false; + + Prog.SubProgress(0, Info(TranslationFile.c_str())); + if (Gen.SelectFile(TranslationFile,string(),*this) == false) + return _error->Error("Problem with SelectFile %s",TranslationFile.c_str()); + + // Store the IMS information + pkgCache::PkgFileIterator TransFile = Gen.GetCurFile(); + struct stat TransSt; + if (fstat(Trans.Fd(),&TransSt) != 0) + return _error->Errno("fstat","Failed to stat"); + TransFile->Size = TransSt.st_size; + TransFile->mtime = TransSt.st_mtime; + + if (Gen.MergeList(TransParser) == false) + return _error->Error("Problem with MergeList %s",TranslationFile.c_str()); + } + + return true; +} + /*}}}*/ +// TranslationsIndex::FindInCache - Find this index /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgCache::PkgFileIterator debTranslationsIndex::FindInCache(pkgCache &Cache) const +{ + string FileName = IndexFile(LanguageCode().c_str()); + + pkgCache::PkgFileIterator File = Cache.FileBegin(); + for (; File.end() == false; File++) + { + if (FileName != File.FileName()) + continue; + + struct stat St; + if (stat(File.FileName(),&St) != 0) + return pkgCache::PkgFileIterator(Cache); + if ((unsigned)St.st_size != File->Size || St.st_mtime != File->mtime) + return pkgCache::PkgFileIterator(Cache); + return File; + } + return File; +} + /*}}}*/ // StatusIndex::debStatusIndex - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -412,6 +576,11 @@ class debIFTypePkg : public pkgIndexFile::Type }; debIFTypePkg() {Label = "Debian Package Index";}; }; +class debIFTypeTrans : public debIFTypePkg +{ + public: + debIFTypeTrans() {Label = "Debian Translation Index";}; +}; class debIFTypeStatus : public pkgIndexFile::Type { public: @@ -424,6 +593,7 @@ class debIFTypeStatus : public pkgIndexFile::Type }; static debIFTypeSrc _apt_Src; static debIFTypePkg _apt_Pkg; +static debIFTypeTrans _apt_Trans; static debIFTypeStatus _apt_Status; const pkgIndexFile::Type *debSourcesIndex::GetType() const @@ -434,6 +604,10 @@ const pkgIndexFile::Type *debPackagesIndex::GetType() const { return &_apt_Pkg; } +const pkgIndexFile::Type *debTranslationsIndex::GetType() const +{ + return &_apt_Trans; +} const pkgIndexFile::Type *debStatusIndex::GetType() const { return &_apt_Status; diff --git a/apt-pkg/deb/debindexfile.h b/apt-pkg/deb/debindexfile.h index 2bc963567..b0012c96b 100644 --- a/apt-pkg/deb/debindexfile.h +++ b/apt-pkg/deb/debindexfile.h @@ -72,6 +72,36 @@ class debPackagesIndex : public pkgIndexFile debPackagesIndex(string URI,string Dist,string Section,bool Trusted); }; +class debTranslationsIndex : public pkgIndexFile +{ + string URI; + string Dist; + string Section; + + string Info(const char *Type) const; + string IndexFile(const char *Type) const; + string IndexURI(const char *Type) const; + + inline string TranslationFile() const {return "Translation-" + LanguageCode();}; + + public: + + virtual const Type *GetType() const; + + // Interface for acquire + virtual string Describe(bool Short) const; + virtual bool GetIndexes(pkgAcquire *Owner) const; + + // Interface for the Cache Generator + virtual bool Exists() const; + virtual bool HasPackages() const; + virtual unsigned long Size() const; + virtual bool Merge(pkgCacheGenerator &Gen,OpProgress &Prog) const; + virtual pkgCache::PkgFileIterator FindInCache(pkgCache &Cache) const; + + debTranslationsIndex(string URI,string Dist,string Section); +}; + class debSourcesIndex : public pkgIndexFile { string URI; diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc index d0dc7a260..074abea6d 100644 --- a/apt-pkg/deb/deblistparser.cc +++ b/apt-pkg/deb/deblistparser.cc @@ -15,6 +15,7 @@ #include <apt-pkg/configuration.h> #include <apt-pkg/strutl.h> #include <apt-pkg/crc-16.h> +#include <apt-pkg/md5.h> #include <ctype.h> @@ -104,6 +105,8 @@ bool debListParser::NewVersion(pkgCache::VerIterator Ver) return false; if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false) return false; + if (ParseDepends(Ver,"Breaks",pkgCache::Dep::DpkgBreaks) == false) + return false; if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false) return false; @@ -117,6 +120,48 @@ bool debListParser::NewVersion(pkgCache::VerIterator Ver) return true; } /*}}}*/ +// ListParser::Description - Return the description string /*{{{*/ +// --------------------------------------------------------------------- +/* This is to return the string describing the package in debian + form. If this returns the blank string then the entry is assumed to + only describe package properties */ +string debListParser::Description() +{ + if (DescriptionLanguage().empty()) + return Section.FindS("Description"); + else + return Section.FindS(("Description-" + pkgIndexFile::LanguageCode()).c_str()); +} + /*}}}*/ +// ListParser::DescriptionLanguage - Return the description lang string /*{{{*/ +// --------------------------------------------------------------------- +/* This is to return the string describing the language of + description. If this returns the blank string then the entry is + assumed to describe original description. */ +string debListParser::DescriptionLanguage() +{ + return Section.FindS("Description").empty() ? pkgIndexFile::LanguageCode() : ""; +} + /*}}}*/ +// ListParser::Description - Return the description_md5 MD5SumValue /*{{{*/ +// --------------------------------------------------------------------- +/* This is to return the md5 string to allow the check if it is the right + description. If no Description-md5 is found in the section it will be + calculated. + */ +MD5SumValue debListParser::Description_md5() +{ + string value = Section.FindS("Description-md5"); + + if (value.empty()) + { + MD5Summation md5; + md5.Add((Description() + "\n").c_str()); + return md5.Result(); + } else + return MD5SumValue(value); +} + /*}}}*/ // ListParser::UsePackage - Update a package structure /*{{{*/ // --------------------------------------------------------------------- /* This is called to update the package with any new information @@ -150,6 +195,7 @@ unsigned short debListParser::VersionHash() // "Suggests", // "Recommends", "Conflicts", + "Breaks", "Replaces",0}; unsigned long Result = INIT_FCS; char S[1024]; diff --git a/apt-pkg/deb/deblistparser.h b/apt-pkg/deb/deblistparser.h index 3a0e0421b..34bb29c72 100644 --- a/apt-pkg/deb/deblistparser.h +++ b/apt-pkg/deb/deblistparser.h @@ -12,6 +12,7 @@ #define PKGLIB_DEBLISTPARSER_H #include <apt-pkg/pkgcachegen.h> +#include <apt-pkg/indexfile.h> #include <apt-pkg/tagfile.h> class debListParser : public pkgCacheGenerator::ListParser @@ -47,6 +48,9 @@ class debListParser : public pkgCacheGenerator::ListParser virtual string Package(); virtual string Version(); virtual bool NewVersion(pkgCache::VerIterator Ver); + virtual string Description(); + virtual string DescriptionLanguage(); + virtual MD5SumValue Description_md5(); virtual unsigned short VersionHash(); virtual bool UsePackage(pkgCache::PkgIterator Pkg, pkgCache::VerIterator Ver); diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc index 6830f9e83..73b2dda49 100644 --- a/apt-pkg/deb/debmetaindex.cc +++ b/apt-pkg/deb/debmetaindex.cc @@ -153,6 +153,16 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool GetAll) const ComputeIndexTargets(), new indexRecords (Dist)); + // Queue the translations + for (vector<const debSectionEntry *>::const_iterator I = SectionEntries.begin(); + I != SectionEntries.end(); I++) { + + if((*I)->IsSrc) + continue; + debTranslationsIndex i = debTranslationsIndex(URI,Dist,(*I)->Section); + i.GetIndexes(Owner); + } + return true; } @@ -177,11 +187,16 @@ vector <pkgIndexFile *> *debReleaseIndex::GetIndexFiles() Indexes = new vector <pkgIndexFile*>; for (vector<const debSectionEntry *>::const_iterator I = SectionEntries.begin(); - I != SectionEntries.end(); I++) + I != SectionEntries.end(); I++) { if ((*I)->IsSrc) Indexes->push_back(new debSourcesIndex (URI, Dist, (*I)->Section, IsTrusted())); else + { Indexes->push_back(new debPackagesIndex (URI, Dist, (*I)->Section, IsTrusted())); + Indexes->push_back(new debTranslationsIndex(URI, Dist, (*I)->Section)); + } + } + return Indexes; } diff --git a/apt-pkg/deb/debrecords.cc b/apt-pkg/deb/debrecords.cc index 6ecbd15d6..8a5b6e215 100644 --- a/apt-pkg/deb/debrecords.cc +++ b/apt-pkg/deb/debrecords.cc @@ -9,7 +9,9 @@ /*}}}*/ // Include Files /*{{{*/ #include <apt-pkg/debrecords.h> +#include <apt-pkg/strutl.h> #include <apt-pkg/error.h> +#include <langinfo.h> /*}}}*/ // RecordParser::debRecordParser - Constructor /*{{{*/ @@ -28,6 +30,10 @@ bool debRecordParser::Jump(pkgCache::VerFileIterator const &Ver) { return Tags.Jump(Section,Ver->Offset); } +bool debRecordParser::Jump(pkgCache::DescFileIterator const &Desc) +{ + return Tags.Jump(Section,Desc->Offset); +} /*}}}*/ // RecordParser::FileName - Return the archive filename on the site /*{{{*/ // --------------------------------------------------------------------- @@ -58,7 +64,7 @@ string debRecordParser::MD5Hash() /* */ string debRecordParser::SHA1Hash() { - return Section.FindS("SHA1Sum"); + return Section.FindS("SHA1"); } /*}}}*/ // RecordParser::Maintainer - Return the maintainer email /*{{{*/ @@ -74,7 +80,7 @@ string debRecordParser::Maintainer() /* */ string debRecordParser::ShortDesc() { - string Res = Section.FindS("Description"); + string Res = LongDesc(); string::size_type Pos = Res.find('\n'); if (Pos == string::npos) return Res; @@ -86,21 +92,60 @@ string debRecordParser::ShortDesc() /* */ string debRecordParser::LongDesc() { - return Section.FindS("Description"); + string orig, dest; + char *codeset = nl_langinfo(CODESET); + + if (!Section.FindS("Description").empty()) + orig = Section.FindS("Description").c_str(); + else + orig = Section.FindS(("Description-" + pkgIndexFile::LanguageCode()).c_str()).c_str(); + + if (strcmp(codeset,"UTF-8") != 0) { + UTF8ToCodeset(codeset, orig, &dest); + orig = dest; + } + + return orig; } /*}}}*/ + +static const char *SourceVerSeparators = " ()"; + // RecordParser::SourcePkg - Return the source package name if any /*{{{*/ // --------------------------------------------------------------------- /* */ string debRecordParser::SourcePkg() { string Res = Section.FindS("Source"); - string::size_type Pos = Res.find(' '); + string::size_type Pos = Res.find_first_of(SourceVerSeparators); if (Pos == string::npos) return Res; return string(Res,0,Pos); } /*}}}*/ +// RecordParser::SourceVer - Return the source version number if present /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string debRecordParser::SourceVer() +{ + string Pkg = Section.FindS("Source"); + string::size_type Pos = Pkg.find_first_of(SourceVerSeparators); + if (Pos == string::npos) + return ""; + + string::size_type VerStart = Pkg.find_first_not_of(SourceVerSeparators, Pos); + if(VerStart == string::npos) + return ""; + + string::size_type VerEnd = Pkg.find_first_of(SourceVerSeparators, VerStart); + if(VerEnd == string::npos) + // Corresponds to the case of, e.g., "foo (1.2" without a closing + // paren. Be liberal and guess what it means. + return string(Pkg, VerStart); + else + return string(Pkg, VerStart, VerEnd - VerStart); +} + /*}}}*/ // RecordParser::GetRec - Return the whole record /*{{{*/ // --------------------------------------------------------------------- /* */ diff --git a/apt-pkg/deb/debrecords.h b/apt-pkg/deb/debrecords.h index 3f1956164..df21931a8 100644 --- a/apt-pkg/deb/debrecords.h +++ b/apt-pkg/deb/debrecords.h @@ -15,6 +15,7 @@ #define PKGLIB_DEBRECORDS_H #include <apt-pkg/pkgrecords.h> +#include <apt-pkg/indexfile.h> #include <apt-pkg/tagfile.h> class debRecordParser : public pkgRecords::Parser @@ -26,6 +27,7 @@ class debRecordParser : public pkgRecords::Parser protected: virtual bool Jump(pkgCache::VerFileIterator const &Ver); + virtual bool Jump(pkgCache::DescFileIterator const &Desc); public: @@ -34,6 +36,7 @@ class debRecordParser : public pkgRecords::Parser virtual string MD5Hash(); virtual string SHA1Hash(); virtual string SourcePkg(); + virtual string SourceVer(); // These are some general stats about the package virtual string Maintainer(); diff --git a/apt-pkg/deb/debsrcrecords.cc b/apt-pkg/deb/debsrcrecords.cc index 21d7392a0..fcd9bb901 100644 --- a/apt-pkg/deb/debsrcrecords.cc +++ b/apt-pkg/deb/debsrcrecords.cc @@ -14,6 +14,8 @@ #include <apt-pkg/error.h> #include <apt-pkg/strutl.h> #include <apt-pkg/configuration.h> + +using std::max; /*}}}*/ // SrcRecordParser::Binaries - Return the binaries field /*{{{*/ @@ -30,31 +32,19 @@ const char **debSrcRecordParser::Binaries() if (Bins.empty() == true || Bins.length() >= 102400) return 0; - // Workaround for #236688. Only allocate a new buffer if the field - // is large, to avoid a performance penalty - char *BigBuf = NULL; - char *Buf; - if (Bins.length() > sizeof(Buffer)) - { - BigBuf = new char[Bins.length()]; - Buf = BigBuf; - } - else + if (Bins.length() >= BufSize) { - Buf = Buffer; + delete [] Buffer; + // allocate new size based on buffer (but never smaller than 4000) + BufSize = max((unsigned int)4000, max((unsigned int)Bins.length()+1,2*BufSize)); + Buffer = new char[BufSize]; } - strcpy(Buf,Bins.c_str()); - if (TokSplitString(',',Buf,StaticBinList, + strcpy(Buffer,Bins.c_str()); + if (TokSplitString(',',Buffer,StaticBinList, sizeof(StaticBinList)/sizeof(StaticBinList[0])) == false) - { - if (BigBuf != NULL) - delete BigBuf; return 0; - } - if (BigBuf != NULL) - delete BigBuf; return (const char **)StaticBinList; } /*}}}*/ diff --git a/apt-pkg/deb/debsrcrecords.h b/apt-pkg/deb/debsrcrecords.h index 51e063812..8b1237ccd 100644 --- a/apt-pkg/deb/debsrcrecords.h +++ b/apt-pkg/deb/debsrcrecords.h @@ -21,9 +21,10 @@ class debSrcRecordParser : public pkgSrcRecords::Parser FileFd Fd; pkgTagFile Tags; pkgTagSection Sect; - char Buffer[10000]; char *StaticBinList[400]; unsigned long iOffset; + char *Buffer; + unsigned int BufSize; public: @@ -46,10 +47,9 @@ class debSrcRecordParser : public pkgSrcRecords::Parser }; virtual bool Files(vector<pkgSrcRecords::File> &F); - debSrcRecordParser(string File,pkgIndexFile const *Index) : - Parser(Index), - Fd(File,FileFd::ReadOnly), - Tags(&Fd,102400) {}; + debSrcRecordParser(string File,pkgIndexFile const *Index) + : Parser(Index), Fd(File,FileFd::ReadOnly), Tags(&Fd,102400), + Buffer(0), BufSize(0) {} }; #endif diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 905ac93c4..e5506d3bf 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -471,6 +471,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) case Item::Install: Args[n++] = "--unpack"; Size += strlen(Args[n-1]); + Args[n++] = "--auto-deconfigure"; + Size += strlen(Args[n-1]); break; } @@ -621,15 +623,22 @@ bool pkgDPkgPM::Go(int OutStatusFd) */ char* list[5]; - if(!TokSplitString(':', line, list, sizeof(list)/sizeof(list[0]))) - // FIXME: dpkg sends multiline error messages sometimes (see - // #374195 for a example. we should support this by - // either patching dpkg to not send multiline over the - // statusfd or by rewriting the code here to deal with - // it. for now we just ignore it and not crash - continue; + // dpkg sends multiline error messages sometimes (see + // #374195 for a example. we should support this by + // either patching dpkg to not send multiline over the + // statusfd or by rewriting the code here to deal with + // it. for now we just ignore it and not crash + TokSplitString(':', line, list, sizeof(list)/sizeof(list[0])); char *pkg = list[1]; char *action = _strstrip(list[2]); + if( pkg == NULL || action == NULL) + { + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "ignoring line: not enough ':'" << std::endl; + // reset the line buffer + line[0]=0; + continue; + } if(strncmp(action,"error",strlen("error")) == 0) { @@ -701,14 +710,23 @@ bool pkgDPkgPM::Go(int OutStatusFd) // Check for an error code. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0) { - RunScripts("DPkg::Post-Invoke"); - if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) - return _error->Error("Sub-process %s received a segmentation fault.",Args[0]); - - if (WIFEXITED(Status) != 0) - return _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); + // if it was set to "keep-dpkg-runing" then we won't return + // here but keep the loop going and just report it as a error + // for later + bool stopOnError = _config->FindB("Dpkg::StopOnError",true); - return _error->Error("Sub-process %s exited unexpectedly",Args[0]); + if(stopOnError) + RunScripts("DPkg::Post-Invoke"); + + if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) + _error->Error("Sub-process %s received a segmentation fault.",Args[0]); + else if (WIFEXITED(Status) != 0) + _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); + else + _error->Error("Sub-process %s exited unexpectedly",Args[0]); + + if(stopOnError) + return false; } } diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h index 45eb6b15b..7da729904 100644 --- a/apt-pkg/deb/dpkgpm.h +++ b/apt-pkg/deb/dpkgpm.h @@ -43,7 +43,7 @@ class pkgDPkgPM : public pkgPackageManager bool RunScripts(const char *Cnf); bool RunScriptsWithPkgs(const char *Cnf); bool SendV2Pkgs(FILE *F); - + // The Actuall installation implementation virtual bool Install(PkgIterator Pkg,string File); virtual bool Configure(PkgIterator Pkg); diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 70bc9f086..fe981ea76 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -13,16 +13,54 @@ #include <apt-pkg/error.h> #include <apt-pkg/sptr.h> #include <apt-pkg/algorithms.h> + +#include <apt-pkg/fileutl.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/pkgsystem.h> +#include <apt-pkg/tagfile.h> + +#include <iostream> +#include <sstream> +#include <set> + +#include <sys/stat.h> #include <apti18n.h> - /*}}}*/ + +pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) : + cache(cache), released(false) +{ + ++cache.group_level; +} + +void pkgDepCache::ActionGroup::release() +{ + if(!released) + { + if(cache.group_level == 0) + std::cerr << "W: Unbalanced action groups, expect badness" << std::endl; + else + { + --cache.group_level; + + if(cache.group_level == 0) + cache.MarkAndSweep(); + } + + released = false; + } +} + +pkgDepCache::ActionGroup::~ActionGroup() +{ + release(); +} // DepCache::pkgDepCache - Constructors /*{{{*/ // --------------------------------------------------------------------- /* */ pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) : - Cache(pCache), PkgState(0), DepState(0) + group_level(0), Cache(pCache), PkgState(0), DepState(0) { delLocalPolicy = 0; LocalPolicy = Plcy; @@ -45,6 +83,10 @@ pkgDepCache::~pkgDepCache() /* This allocats the extension buffers and initializes them. */ bool pkgDepCache::Init(OpProgress *Prog) { + // Suppress mark updates during this operation (just in case) and + // run a mark operation when Init terminates. + ActionGroup actions(*this); + delete [] PkgState; delete [] DepState; PkgState = new StateCache[Head().PackageCount]; @@ -70,7 +112,7 @@ bool pkgDepCache::Init(OpProgress *Prog) // 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(); @@ -92,11 +134,134 @@ bool pkgDepCache::Init(OpProgress *Prog) if(Prog != 0) Prog->Done(); - + return true; } /*}}}*/ +bool pkgDepCache::readStateFile(OpProgress *Prog) +{ + FileFd state_file; + string state = _config->FindDir("Dir::State") + "extended_states"; + if(FileExists(state)) { + state_file.Open(state, FileFd::ReadOnly); + int file_size = state_file.Size(); + if(Prog != NULL) + Prog->OverallProgress(0, file_size, 1, + _("Reading state information")); + + pkgTagFile tagfile(&state_file); + pkgTagSection section; + int amt=0; + while(tagfile.Step(section)) { + string pkgname = section.FindS("Package"); + pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname); + // Silently ignore unknown packages and packages with no actual + // version. + if(!pkg.end() && !pkg.VersionList().end()) { + short reason = section.FindI("Auto-Installed", 0); + if(reason > 0) + PkgState[pkg->ID].Flags |= Flag::Auto; + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::cout << "Auto-Installed : " << pkgname << std::endl; + amt+=section.size(); + if(Prog != NULL) + Prog->OverallProgress(amt, file_size, 1, + _("Reading state information")); + } + if(Prog != NULL) + Prog->OverallProgress(file_size, file_size, 1, + _("Reading state information")); + } + } + + return true; +} + +bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) +{ + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::clog << "pkgDepCache::writeStateFile()" << std::endl; + + FileFd StateFile; + string state = _config->FindDir("Dir::State") + "extended_states"; + + // if it does not exist, create a empty one + if(!FileExists(state)) + { + StateFile.Open(state, FileFd::WriteEmpty); + StateFile.Close(); + } + + // open it + if(!StateFile.Open(state, FileFd::ReadOnly)) + return _error->Error(_("Failed to open StateFile %s"), + state.c_str()); + + FILE *OutFile; + string outfile = state + ".tmp"; + if((OutFile = fopen(outfile.c_str(),"w")) == NULL) + return _error->Error(_("Failed to write temporary StateFile %s"), + outfile.c_str()); + + // first merge with the existing sections + pkgTagFile tagfile(&StateFile); + pkgTagSection section; + std::set<string> pkgs_seen; + const char *nullreorderlist[] = {0}; + while(tagfile.Step(section)) { + string pkgname = section.FindS("Package"); + // Silently ignore unknown packages and packages with no actual + // version. + pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname); + if(pkg.end() || pkg.VersionList().end()) + continue; + bool oldAuto = section.FindI("Auto-Installed"); + bool newAuto = (PkgState[pkg->ID].Flags & Flag::Auto); + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::clog << "Update exisiting AutoInstall info: " + << pkg.Name() << std::endl; + TFRewriteData rewrite[2]; + rewrite[0].Tag = "Auto-Installed"; + rewrite[0].Rewrite = newAuto ? "1" : "0"; + rewrite[0].NewTag = 0; + rewrite[1].Tag = 0; + TFRewrite(OutFile, section, nullreorderlist, rewrite); + fprintf(OutFile,"\n"); + pkgs_seen.insert(pkgname); + } + + // then write the ones we have not seen yet + std::ostringstream ostr; + for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); pkg++) { + if(PkgState[pkg->ID].Flags & Flag::Auto) { + if (pkgs_seen.find(pkg.Name()) != pkgs_seen.end()) { + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::clog << "Skipping already written " << pkg.Name() << std::endl; + continue; + } + // skip not installed ones if requested + if(InstalledOnly && pkg->CurrentVer == 0) + continue; + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::clog << "Writing new AutoInstall: " + << pkg.Name() << std::endl; + ostr.str(string("")); + ostr << "Package: " << pkg.Name() + << "\nAuto-Installed: 1\n\n"; + fprintf(OutFile,ostr.str().c_str()); + fprintf(OutFile,"\n"); + } + } + fclose(OutFile); + + // move the outfile over the real file and set permissions + rename(outfile.c_str(), state.c_str()); + chmod(state.c_str(), 0644); + + return true; +} + // DepCache::CheckDep - Checks a single dependency /*{{{*/ // --------------------------------------------------------------------- /* This first checks the dependency against the main target package and @@ -111,7 +276,7 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) 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 && Dep->Type != Dep::Obsoletes)) + (Dep->Type != Dep::Conflicts && Dep->Type != Dep::DpkgBreaks && Dep->Type != Dep::Obsoletes)) { PkgIterator Pkg = Dep.TargetPkg(); // Check the base package @@ -141,7 +306,8 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) { /* 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) + if (P.OwnerPkg() == Pkg && + (Dep->Type == Dep::Conflicts || Dep->Type == Dep::DpkgBreaks)) continue; // Check if the provides is a hit @@ -237,9 +403,11 @@ void pkgDepCache::AddStates(const PkgIterator &Pkg,int Add) { StateCache &State = PkgState[Pkg->ID]; - // The Package is broken + // The Package is broken (either minimal dep or policy dep) if ((State.DepState & DepInstMin) != DepInstMin) iBrokenCount += Add; + if ((State.DepState & DepInstPolicy) != DepInstPolicy) + iPolicyBrokenCount += Add; // Bad state if (Pkg.State() != PkgIterator::NeedsNothing) @@ -293,7 +461,9 @@ void pkgDepCache::BuildGroupOrs(VerIterator const &V) /* Invert for Conflicts. We have to do this twice to get the right sense for a conflicts group */ - if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes) + if (D->Type == Dep::Conflicts || + D->Type == Dep::DpkgBreaks || + D->Type == Dep::Obsoletes) State = ~State; // Add to the group if we are within an or.. @@ -304,7 +474,9 @@ void pkgDepCache::BuildGroupOrs(VerIterator const &V) Group = 0; // Invert for Conflicts - if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes) + if (D->Type == Dep::Conflicts || + D->Type == Dep::DpkgBreaks || + D->Type == Dep::Obsoletes) State = ~State; } } @@ -437,7 +609,9 @@ void pkgDepCache::Update(OpProgress *Prog) Group = 0; // Invert for Conflicts - if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes) + if (D->Type == Dep::Conflicts || + D->Type == Dep::DpkgBreaks || + D->Type == Dep::Obsoletes) State = ~State; } } @@ -450,12 +624,14 @@ void pkgDepCache::Update(OpProgress *Prog) if (Prog != 0) Prog->Progress(Done); + + readStateFile(Prog); } /*}}}*/ // 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. */ + It is mainly meant to scan reverse dependencies. */ void pkgDepCache::Update(DepIterator D) { // Update the reverse deps @@ -465,7 +641,9 @@ void pkgDepCache::Update(DepIterator D) State = DependencyState(D); // Invert for Conflicts - if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes) + if (D->Type == Dep::Conflicts || + D->Type == Dep::DpkgBreaks || + D->Type == Dep::Obsoletes) State = ~State; RemoveStates(D.ParentPkg()); @@ -507,7 +685,7 @@ void pkgDepCache::Update(PkgIterator const &Pkg) // DepCache::MarkKeep - Put the package in the keep state /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) +void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser) { // Simplifies other routines. if (Pkg.end() == true) @@ -519,6 +697,9 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) Pkg.CurrentVer().Downloadable() == false) return; + /** \todo Can this be moved later in the method? */ + ActionGroup group(*this); + /* We changed the soft state all the time so the UI is a bit nicer to use */ StateCache &P = PkgState[Pkg->ID]; @@ -534,8 +715,17 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) // We dont even try to keep virtual packages.. if (Pkg->VersionList == 0) return; - - P.Flags &= ~Flag::Auto; +#if 0 // reseting the autoflag here means we lose the + // auto-mark information if a user selects a package for removal + // but changes his mind then and sets it for keep again + // - this makes sense as default when all Garbage dependencies + // are automatically marked for removal (as aptitude does). + // setting a package for keep then makes it no longer autoinstalled + // for all other use-case this action is rather suprising + if(FromUser && !P.Marked) + P.Flags &= ~Flag::Auto; +#endif + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -561,6 +751,8 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) if (Pkg.end() == true) return; + ActionGroup group(*this); + // Check that it is not already marked for delete StateCache &P = PkgState[Pkg->ID]; P.iFlags &= ~(AutoKept | Purge); @@ -583,7 +775,6 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) else P.Mode = ModeDelete; P.InstallVer = 0; - P.Flags &= Flag::Auto; AddStates(Pkg); Update(Pkg); @@ -594,7 +785,8 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) // --------------------------------------------------------------------- /* */ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, - unsigned long Depth) + unsigned long Depth, bool FromUser, + bool ForceImportantDeps) { if (Depth > 100) return; @@ -603,26 +795,27 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, if (Pkg.end() == true) return; + ActionGroup group(*this); + /* 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 || + if ((P.InstPolicyBroken() == false && P.InstBroken() == false) && + (P.Mode == ModeInstall || P.CandidateVer == (Version *)Pkg.CurrentVer())) { if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0) - MarkKeep(Pkg); + MarkKeep(Pkg, false, FromUser); return; } // See if there is even any possible instalation candidate if (P.CandidateVer == 0) 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 */ @@ -631,7 +824,20 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, P.Mode = ModeInstall; P.InstallVer = P.CandidateVer; - P.Flags &= ~Flag::Auto; + + if(FromUser) + { + // Set it to manual if it's a new install or cancelling the + // removal of a garbage package. + if(P.Status == 2 || (!Pkg.CurrentVer().end() && !P.Marked)) + P.Flags &= ~Flag::Auto; + } + else + { + // Set it to auto if this is a new install. + if(P.Status == 2) + P.Flags |= Flag::Auto; + } if (P.CandidateVer == (Version *)Pkg.CurrentVer()) P.Mode = ModeKeep; @@ -663,10 +869,43 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* 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 */ + it will be installed. Otherwise we only check for important + deps that have changed from the installed version + */ if (IsImportantDep(Start) == false) continue; - if (Pkg->CurrentVer != 0 && Start.IsCritical() == false) + + /* check if any ImportantDep() (but not Critial) where added + * since we installed the package + */ + bool isNewImportantDep = false; + if(!ForceImportantDeps && !Start.IsCritical()) + { + bool found=false; + VerIterator instVer = Pkg.CurrentVer(); + if(!instVer.end()) + { + for (DepIterator D = instVer.DependsList(); D.end() != true; D++) + { + //FIXME: deal better with or-groups(?) + DepIterator LocalStart = D; + + if(IsImportantDep(D) && Start.TargetPkg() == D.TargetPkg()) + found=true; + } + // this is a new dep if it was not found to be already + // a important dep of the installed pacakge + isNewImportantDep = !found; + } + } + if(isNewImportantDep) + if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) + std::clog << "new important dependency: " + << Start.TargetPkg().Name() << std::endl; + + // skip important deps if the package is already installed + if (Pkg->CurrentVer != 0 && Start.IsCritical() == false + && !isNewImportantDep && !ForceImportantDeps) continue; /* If we are in an or group locate the first or that can @@ -677,7 +916,8 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* This bit is for processing the possibilty of an install/upgrade fixing the problem */ SPtrArray<Version *> List = Start.AllTargets(); - if ((DepState[Start->ID] & DepCVer) == DepCVer) + if (Start->Type != Dep::DpkgBreaks && + (DepState[Start->ID] & DepCVer) == DepCVer) { // Right, find the best version to install.. Version **Cur = List; @@ -708,33 +948,48 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, } } - if (InstPkg.end() == false) + if (InstPkg.end() == false) { if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) std::clog << "Installing " << InstPkg.Name() << " as dep of " << Pkg.Name() << std::endl; - MarkInstall(InstPkg,true,Depth + 1); - - // Set the autoflag, after MarkInstall because MarkInstall unsets it - if (P->CurrentVer == 0) - PkgState[InstPkg->ID].Flags |= Flag::Auto; + // now check if we should consider it a automatic dependency or not + string sec = _config->Find("APT::Never-MarkAuto-Section",""); + if(Pkg.Section() && (string(Pkg.Section()) == sec)) + { + if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) + std::clog << "Setting NOT as auto-installed because its a direct dep of a package in section " << sec << std::endl; + MarkInstall(InstPkg,true,Depth + 1, true); + } + else + { + // mark automatic dependency + MarkInstall(InstPkg,true,Depth + 1, false, ForceImportantDeps); + // Set the autoflag, after MarkInstall because MarkInstall unsets it + if (P->CurrentVer == 0) + PkgState[InstPkg->ID].Flags |= Flag::Auto; + } } - continue; } - + /* For conflicts we just de-install the package and mark as auto, - Conflicts may not have or groups */ - if (Start->Type == Dep::Conflicts || Start->Type == Dep::Obsoletes) + Conflicts may not have or groups. For dpkg's Breaks we try to + upgrade the package. */ + if (Start->Type == Dep::Conflicts || Start->Type == Dep::Obsoletes || + Start->Type == Dep::DpkgBreaks) { for (Version **I = List; *I != 0; I++) { VerIterator Ver(*this,*I); PkgIterator Pkg = Ver.ParentPkg(); - - MarkDelete(Pkg); - PkgState[Pkg->ID].Flags |= Flag::Auto; + + if (Start->Type != Dep::DpkgBreaks) + MarkDelete(Pkg); + else + if (PkgState[Pkg->ID].CandidateVer != *I) + MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps); } continue; } @@ -746,6 +1001,8 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* */ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) { + ActionGroup group(*this); + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -764,9 +1021,11 @@ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) /* */ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer) { + ActionGroup group(*this); + pkgCache::PkgIterator Pkg = TargetVer.ParentPkg(); StateCache &P = PkgState[Pkg->ID]; - + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -779,6 +1038,18 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer) Update(Pkg); AddSizes(Pkg); } + +void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto) +{ + StateCache &state = PkgState[Pkg->ID]; + + ActionGroup group(*this); + + if(Auto) + state.Flags |= Flag::Auto; + else + state.Flags &= ~Flag::Auto; +} /*}}}*/ // StateCache::Update - Compute the various static display things /*{{{*/ // --------------------------------------------------------------------- @@ -866,6 +1137,244 @@ pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator Pkg) /* */ bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep) { - return Dep.IsCritical(); + if(Dep.IsCritical()) + return true; + else if(Dep->Type == pkgCache::Dep::Recommends) + { + if ( _config->FindB("APT::Install-Recommends", false)) + return true; + // we suport a special mode to only install-recommends for certain + // sections + // FIXME: this is a meant as a temporarly solution until the + // recommends are cleaned up + string s = _config->Find("APT::Install-Recommends-Section",""); + if(s.size() > 0) + { + const char *sec = Dep.TargetPkg().Section(); + if (sec && strcmp(sec, s.c_str()) == 0) + return true; + } + } + else if(Dep->Type == pkgCache::Dep::Suggests) + return _config->FindB("APT::Install-Suggests", false); + + return false; } /*}}}*/ + +pkgDepCache::DefaultRootSetFunc::DefaultRootSetFunc() + : constructedSuccessfully(false) +{ + Configuration::Item const *Opts; + Opts = _config->Tree("APT::NeverAutoRemove"); + if (Opts != 0 && Opts->Child != 0) + { + Opts = Opts->Child; + for (; Opts != 0; Opts = Opts->Next) + { + if (Opts->Value.empty() == true) + continue; + + regex_t *p = new regex_t; + if(regcomp(p,Opts->Value.c_str(), + REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) + { + regfree(p); + delete p; + _error->Error("Regex compilation error for APT::NeverAutoRemove"); + return; + } + + rootSetRegexp.push_back(p); + } + } + + constructedSuccessfully = true; +} + +pkgDepCache::DefaultRootSetFunc::~DefaultRootSetFunc() +{ + for(unsigned int i = 0; i < rootSetRegexp.size(); i++) + { + regfree(rootSetRegexp[i]); + delete rootSetRegexp[i]; + } +} + + +bool pkgDepCache::DefaultRootSetFunc::InRootSet(const pkgCache::PkgIterator &pkg) +{ + for(unsigned int i = 0; i < rootSetRegexp.size(); i++) + if (regexec(rootSetRegexp[i], pkg.Name(), 0, 0, 0) == 0) + return true; + + return false; +} + +pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc() +{ + DefaultRootSetFunc *f = new DefaultRootSetFunc; + if(f->wasConstructedSuccessfully()) + return f; + else + { + delete f; + return NULL; + } +} + +bool pkgDepCache::MarkFollowsRecommends() +{ + return _config->FindB("APT::AutoRemove::RecommendsImportant", true); +} + +bool pkgDepCache::MarkFollowsSuggests() +{ + return _config->FindB("APT::AutoRemove::SuggestsImportant", false); +} + +// the main mark algorithm +bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc) +{ + bool follow_recommends; + bool follow_suggests; + + // init the states + for(PkgIterator p = PkgBegin(); !p.end(); ++p) + { + PkgState[p->ID].Marked = false; + PkgState[p->ID].Garbage = false; + + // debug output + if(_config->FindB("Debug::pkgAutoRemove",false) + && PkgState[p->ID].Flags & Flag::Auto) + std::clog << "AutoDep: " << p.Name() << std::endl; + } + + // init vars + follow_recommends = MarkFollowsRecommends(); + follow_suggests = MarkFollowsSuggests(); + + + + // do the mark part, this is the core bit of the algorithm + for(PkgIterator p = PkgBegin(); !p.end(); ++p) + { + if(!(PkgState[p->ID].Flags & Flag::Auto) || + (p->Flags & Flag::Essential) || + userFunc.InRootSet(p)) + + { + // the package is installed (and set to keep) + if(PkgState[p->ID].Keep() && !p.CurrentVer().end()) + MarkPackage(p, p.CurrentVer(), + follow_recommends, follow_suggests); + // the package is to be installed + else if(PkgState[p->ID].Install()) + MarkPackage(p, PkgState[p->ID].InstVerIter(*this), + follow_recommends, follow_suggests); + } + } + + return true; +} + +// mark a single package in Mark-and-Sweep +void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg, + const pkgCache::VerIterator &ver, + bool follow_recommends, + bool follow_suggests) +{ + pkgDepCache::StateCache &state = PkgState[pkg->ID]; + VerIterator candver = state.CandidateVerIter(*this); + VerIterator instver = state.InstVerIter(*this); + +#if 0 + // If a package was garbage-collected but is now being marked, we + // should re-select it + // For cases when a pkg is set to upgrade and this trigger the + // removal of a no-longer used dependency. if the pkg is set to + // keep again later it will result in broken deps + if(state.Delete() && state.RemoveReason = Unused) + { + if(ver==candver) + mark_install(pkg, false, false, NULL); + else if(ver==pkg.CurrentVer()) + MarkKeep(pkg, false, false); + + instver=state.InstVerIter(*this); + } +#endif + + // Ignore versions other than the InstVer, and ignore packages + // that are already going to be removed or just left uninstalled. + if(!(ver == instver && !instver.end())) + return; + + // if we are marked already we are done + if(state.Marked) + return; + + //std::cout << "Setting Marked for: " << pkg.Name() << std::endl; + state.Marked=true; + + if(!ver.end()) + { + for(DepIterator d = ver.DependsList(); !d.end(); ++d) + { + if(d->Type == Dep::Depends || + d->Type == Dep::PreDepends || + (follow_recommends && + d->Type == Dep::Recommends) || + (follow_suggests && + d->Type == Dep::Suggests)) + { + // Try all versions of this package. + for(VerIterator V = d.TargetPkg().VersionList(); + !V.end(); ++V) + { + if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer())) + { + MarkPackage(V.ParentPkg(), V, + follow_recommends, follow_suggests); + } + } + // Now try virtual packages + for(PrvIterator prv=d.TargetPkg().ProvidesList(); + !prv.end(); ++prv) + { + if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp, + d.TargetVer())) + { + MarkPackage(prv.OwnerPkg(), prv.OwnerVer(), + follow_recommends, follow_suggests); + } + } + } + } + } +} + +bool pkgDepCache::Sweep() +{ + // do the sweep + for(PkgIterator p=PkgBegin(); !p.end(); ++p) + { + StateCache &state=PkgState[p->ID]; + + // skip required packages + if (!p.CurrentVer().end() && + (p.CurrentVer()->Priority == pkgCache::State::Required)) + continue; + + // if it is not marked and it is installed, it's garbage + if(!state.Marked && (!p.CurrentVer().end() || state.Install())) + { + state.Garbage=true; + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::cout << "Garbage: " << p.Name() << std::endl; + } + } + + return true; +} diff --git a/apt-pkg/depcache.h b/apt-pkg/depcache.h index 929fe93c9..84fed7f37 100644 --- a/apt-pkg/depcache.h +++ b/apt-pkg/depcache.h @@ -1,4 +1,4 @@ -// -*- mode: cpp; mode: fold -*- +// -*- mode: c++; mode: fold -*- // Description /*{{{*/ // $Id: depcache.h,v 1.14 2001/02/20 07:03:17 jgg Exp $ /* ###################################################################### @@ -42,9 +42,71 @@ #include <apt-pkg/pkgcache.h> #include <apt-pkg/progress.h> +#include <regex.h> + +#include <vector> + class pkgDepCache : protected pkgCache::Namespace { public: + + /** \brief An arbitrary predicate on packages. */ + class InRootSetFunc + { + public: + virtual bool InRootSet(const pkgCache::PkgIterator &pkg) {return false;}; + virtual ~InRootSetFunc() {}; + }; + + private: + /** \brief Mark a single package and all its unmarked important + * dependencies during mark-and-sweep. + * + * Recursively invokes itself to mark all dependencies of the + * package. + * + * \param pkg The package to mark. + * + * \param ver The version of the package that is to be marked. + * + * \param follow_recommends If \b true, recommendations of the + * package will be recursively marked. + * + * \param follow_suggests If \b true, suggestions of the package + * will be recursively marked. + */ + void MarkPackage(const pkgCache::PkgIterator &pkg, + const pkgCache::VerIterator &ver, + bool follow_recommends, + bool follow_suggests); + + /** \brief Update the Marked field of all packages. + * + * Each package's StateCache::Marked field will be set to \b true + * if and only if it can be reached from the root set. By + * default, the root set consists of the set of manually installed + * or essential packages, but it can be extended using the + * parameter #rootFunc. + * + * \param rootFunc A callback that can be used to add extra + * packages to the root set. + * + * \return \b false if an error occured. + */ + bool MarkRequired(InRootSetFunc &rootFunc); + + /** \brief Set the StateCache::Garbage flag on all packages that + * should be removed. + * + * Packages that were not marked by the last call to #MarkRequired + * are tested to see whether they are actually garbage. If so, + * they are marked as such. + * + * \return \b false if an error occured. + */ + bool Sweep(); + + public: // These flags are used in DepState enum DepFlags {DepNow = (1 << 0),DepInstall = (1 << 1),DepCVer = (1 << 2), @@ -60,6 +122,84 @@ class pkgDepCache : protected pkgCache::Namespace enum VersionTypes {NowVersion, InstallVersion, CandidateVersion}; enum ModeList {ModeDelete = 0, ModeKeep = 1, ModeInstall = 2}; + + /** \brief Represents an active action group. + * + * An action group is a group of actions that are currently being + * performed. While an active group is active, certain routine + * clean-up actions that would normally be performed after every + * cache operation are delayed until the action group is + * completed. This is necessary primarily to avoid inefficiencies + * when modifying a large number of packages at once. + * + * This class represents an active action group. Creating an + * instance will create an action group; destroying one will + * destroy the corresponding action group. + * + * The following operations are suppressed by this class: + * + * - Keeping the Marked and Garbage flags up to date. + * + * \note This can be used in the future to easily accumulate + * atomic actions for undo or to display "what apt did anyway"; + * e.g., change the counter of how many action groups are active + * to a std::set of pointers to them and use those to store + * information about what happened in a group in the group. + */ + class ActionGroup + { + pkgDepCache &cache; + + bool released; + + /** Action groups are noncopyable. */ + ActionGroup(const ActionGroup &other); + public: + /** \brief Create a new ActionGroup. + * + * \param cache The cache that this ActionGroup should + * manipulate. + * + * As long as this object exists, no automatic cleanup + * operations will be undertaken. + */ + ActionGroup(pkgDepCache &cache); + + /** \brief Clean up the action group before it is destroyed. + * + * If it is destroyed later, no second cleanup wil be run. + */ + void release(); + + /** \brief Destroy the action group. + * + * If this is the last action group, the automatic cache + * cleanup operations will be undertaken. + */ + ~ActionGroup(); + }; + + /** \brief Returns \b true for packages matching a regular + * expression in APT::NeverAutoRemove. + */ + class DefaultRootSetFunc : public InRootSetFunc + { + std::vector<regex_t *> rootSetRegexp; + bool constructedSuccessfully; + + public: + DefaultRootSetFunc(); + ~DefaultRootSetFunc(); + + /** \return \b true if the class initialized successfully, \b + * false otherwise. Used to avoid throwing an exception, since + * APT classes generally don't. + */ + bool wasConstructedSuccessfully() const { return constructedSuccessfully; } + + bool InRootSet(const pkgCache::PkgIterator &pkg); + }; + struct StateCache { // Epoch stripped text versions of the two version fields @@ -76,6 +216,17 @@ class pkgDepCache : protected pkgCache::Namespace unsigned short Flags; unsigned short iFlags; // Internal flags + /** \brief \b true if this package can be reached from the root set. */ + bool Marked; + + /** \brief \b true if this package is unused and should be removed. + * + * This differs from !#Marked, because it is possible that some + * unreachable packages will be protected from becoming + * garbage. + */ + bool Garbage; + // Various tree indicators signed char Status; // -1,0,1,2 unsigned char Mode; // ModeList @@ -94,7 +245,9 @@ class pkgDepCache : protected pkgCache::Namespace inline bool Downgrade() const {return Status < 0 && Mode == ModeInstall;}; inline bool Held() const {return Status != 0 && Keep();}; inline bool NowBroken() const {return (DepState & DepNowMin) != DepNowMin;}; + inline bool NowPolicyBroken() const {return (DepState & DepNowPolicy) != DepNowPolicy;}; inline bool InstBroken() const {return (DepState & DepInstMin) != DepInstMin;}; + inline bool InstPolicyBroken() const {return (DepState & DepInstPolicy) != DepInstPolicy;}; inline bool Install() const {return Mode == ModeInstall;}; inline VerIterator InstVerIter(pkgCache &Cache) {return VerIterator(Cache,InstallVer);}; @@ -116,6 +269,14 @@ class pkgDepCache : protected pkgCache::Namespace virtual ~Policy() {}; }; + + private: + /** The number of open "action groups"; certain post-action + * operations are suppressed if this number is > 0. + */ + int group_level; + + friend class ActionGroup; protected: @@ -130,6 +291,7 @@ class pkgDepCache : protected pkgCache::Namespace unsigned long iDelCount; unsigned long iKeepCount; unsigned long iBrokenCount; + unsigned long iPolicyBrokenCount; unsigned long iBadCount; Policy *delLocalPolicy; // For memory clean up.. @@ -179,16 +341,69 @@ class pkgDepCache : protected pkgCache::Namespace 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); + /** \return A function identifying packages in the root set other + * than manually installed packages and essential packages, or \b + * NULL if an error occurs. + * + * \todo Is this the best place for this function? Perhaps the + * settings for mark-and-sweep should be stored in a single + * external class? + */ + virtual InRootSetFunc *GetRootSetFunc(); + + /** \return \b true if the garbage collector should follow recommendations. + */ + virtual bool MarkFollowsRecommends(); + + /** \return \b true if the garbage collector should follow suggestions. + */ + virtual bool MarkFollowsSuggests(); + + /** \brief Update the Marked and Garbage fields of all packages. + * + * This routine is implicitly invoked after all state manipulators + * and when an ActionGroup is destroyed. It invokes #MarkRequired + * and #Sweep to do its dirty work. + * + * \param rootFunc A predicate that returns \b true for packages + * that should be added to the root set. + */ + bool MarkAndSweep(InRootSetFunc &rootFunc) + { + return MarkRequired(rootFunc) && Sweep(); + } + + bool MarkAndSweep() + { + std::auto_ptr<InRootSetFunc> f(GetRootSetFunc()); + if(f.get() != NULL) + return MarkAndSweep(*f.get()); + else + return false; + } + + /** \name State Manipulators + */ + // @{ + void MarkKeep(PkgIterator const &Pkg, bool Soft = false, + bool FromUser = true); void MarkDelete(PkgIterator const &Pkg,bool Purge = false); void MarkInstall(PkgIterator const &Pkg,bool AutoInst = true, - unsigned long Depth = 0); + unsigned long Depth = 0, bool FromUser = true, + bool ForceImportantDeps = false); void SetReInstall(PkgIterator const &Pkg,bool To); void SetCandidateVersion(VerIterator TargetVer); + + /** Set the "is automatically installed" flag of Pkg. */ + void MarkAuto(const PkgIterator &Pkg, bool Auto); + // @} // This is for debuging void Update(OpProgress *Prog = 0); + + // read persistent states + bool readStateFile(OpProgress *prog); + bool writeStateFile(OpProgress *prog, bool InstalledOnly=false); // Size queries inline double UsrSize() {return iUsrSize;}; @@ -197,6 +412,7 @@ class pkgDepCache : protected pkgCache::Namespace inline unsigned long KeepCount() {return iKeepCount;}; inline unsigned long InstCount() {return iInstCount;}; inline unsigned long BrokenCount() {return iBrokenCount;}; + inline unsigned long PolicyBrokenCount() {return iPolicyBrokenCount;}; inline unsigned long BadCount() {return iBadCount;}; bool Init(OpProgress *Prog); diff --git a/apt-pkg/indexcopy.cc b/apt-pkg/indexcopy.cc index 1f65062f7..c9dded134 100644 --- a/apt-pkg/indexcopy.cc +++ b/apt-pkg/indexcopy.cc @@ -32,6 +32,8 @@ using namespace std; + + // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -512,10 +514,10 @@ bool SourceCopy::RewriteEntry(FILE *Target,string File) fputc('\n',Target); return true; } - - /*}}}*/ - +// SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/ +// --------------------------------------------------------------------- +/* */ bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex) { const indexRecords::checkSum *Record = MetaIndex->Lookup(file); @@ -670,3 +672,178 @@ bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, return true; } + + +bool TranslationsCopy::CopyTranslations(string CDROM,string Name,vector<string> &List, + pkgCdromStatus *log) +{ + OpProgress *Progress = NULL; + if (List.size() == 0) + return true; + + if(log) + Progress = log->GetOpProgress(); + + bool Debug = _config->FindB("Debug::aptcdrom",false); + + // Prepare the progress indicator + unsigned long TotalSize = 0; + for (vector<string>::iterator I = List.begin(); I != List.end(); I++) + { + struct stat Buf; + if (stat(string(*I).c_str(),&Buf) != 0 && + stat(string(*I + ".gz").c_str(),&Buf) != 0) + return _error->Errno("stat","Stat failed for %s", + string(*I).c_str()); + TotalSize += Buf.st_size; + } + + unsigned long CurrentSize = 0; + unsigned int NotFound = 0; + unsigned int WrongSize = 0; + unsigned int Packages = 0; + for (vector<string>::iterator I = List.begin(); I != List.end(); I++) + { + string OrigPath = string(*I,CDROM.length()); + unsigned long FileSize = 0; + + // Open the package file + FileFd Pkg; + if (FileExists(*I) == true) + { + Pkg.Open(*I,FileFd::ReadOnly); + FileSize = Pkg.Size(); + } + else + { + FileFd From(*I + ".gz",FileFd::ReadOnly); + if (_error->PendingError() == true) + return false; + FileSize = From.Size(); + + // Get a temp file + FILE *tmp = tmpfile(); + if (tmp == 0) + return _error->Errno("tmpfile","Unable to create a tmp file"); + Pkg.Fd(dup(fileno(tmp))); + fclose(tmp); + + // Fork gzip + pid_t Process = fork(); + if (Process < 0) + return _error->Errno("fork","Couldn't fork gzip"); + + // The child + if (Process == 0) + { + dup2(From.Fd(),STDIN_FILENO); + dup2(Pkg.Fd(),STDOUT_FILENO); + SetCloseExec(STDIN_FILENO,false); + SetCloseExec(STDOUT_FILENO,false); + + const char *Args[3]; + string Tmp = _config->Find("Dir::bin::gzip","gzip"); + Args[0] = Tmp.c_str(); + Args[1] = "-d"; + Args[2] = 0; + execvp(Args[0],(char **)Args); + exit(100); + } + + // Wait for gzip to finish + if (ExecWait(Process,_config->Find("Dir::bin::gzip","gzip").c_str(),false) == false) + return _error->Error("gzip failed, perhaps the disk is full."); + + Pkg.Seek(0); + } + pkgTagFile Parser(&Pkg); + if (_error->PendingError() == true) + return false; + + // Open the output file + char S[400]; + snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(), + (*I).c_str() + CDROM.length()); + string TargetF = _config->FindDir("Dir::State::lists") + "partial/"; + TargetF += URItoFileName(S); + if (_config->FindB("APT::CDROM::NoAct",false) == true) + TargetF = "/dev/null"; + FileFd Target(TargetF,FileFd::WriteEmpty); + FILE *TargetFl = fdopen(dup(Target.Fd()),"w"); + if (_error->PendingError() == true) + return false; + if (TargetFl == 0) + return _error->Errno("fdopen","Failed to reopen fd"); + + // Setup the progress meter + if(Progress) + Progress->OverallProgress(CurrentSize,TotalSize,FileSize, + string("Reading Translation Indexes")); + + // Parse + if(Progress) + Progress->SubProgress(Pkg.Size()); + pkgTagSection Section; + this->Section = &Section; + string Prefix; + unsigned long Hits = 0; + unsigned long Chop = 0; + while (Parser.Step(Section) == true) + { + if(Progress) + Progress->Progress(Parser.Offset()); + + const char *Start; + const char *Stop; + Section.GetSection(Start,Stop); + fwrite(Start,Stop-Start, 1, TargetFl); + fputc('\n',TargetFl); + + Packages++; + Hits++; + } + fclose(TargetFl); + + if (Debug == true) + cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl; + + if (_config->FindB("APT::CDROM::NoAct",false) == false) + { + // Move out of the partial directory + Target.Close(); + string FinalF = _config->FindDir("Dir::State::lists"); + FinalF += URItoFileName(S); + if (rename(TargetF.c_str(),FinalF.c_str()) != 0) + return _error->Errno("rename","Failed to rename"); + } + + + CurrentSize += FileSize; + } + if(Progress) + Progress->Done(); + + // Some stats + if(log) { + stringstream msg; + if(NotFound == 0 && WrongSize == 0) + ioprintf(msg, _("Wrote %i records.\n"), Packages); + else if (NotFound != 0 && WrongSize == 0) + ioprintf(msg, _("Wrote %i records with %i missing files.\n"), + Packages, NotFound); + else if (NotFound == 0 && WrongSize != 0) + ioprintf(msg, _("Wrote %i records with %i mismatched files\n"), + Packages, WrongSize); + if (NotFound != 0 && WrongSize != 0) + ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize); + } + + if (Packages == 0) + _error->Warning("No valid records were found."); + + if (NotFound + WrongSize > 10) + _error->Warning("Alot of entries were discarded, something may be wrong.\n"); + + + return true; +} diff --git a/apt-pkg/indexcopy.h b/apt-pkg/indexcopy.h index 4dcb2b46d..7778ae595 100644 --- a/apt-pkg/indexcopy.h +++ b/apt-pkg/indexcopy.h @@ -70,6 +70,17 @@ class SourceCopy : public IndexCopy public: }; +class TranslationsCopy +{ + protected: + pkgTagSection *Section; + + public: + bool CopyTranslations(string CDROM,string Name,vector<string> &List, + pkgCdromStatus *log); +}; + + class SigVerify { bool Verify(string prefix,string file, indexRecords *records); @@ -81,4 +92,6 @@ class SigVerify vector<string> PkgList,vector<string> SrcList); }; + + #endif diff --git a/apt-pkg/indexfile.cc b/apt-pkg/indexfile.cc index 63f692c1d..ca11fc111 100644 --- a/apt-pkg/indexfile.cc +++ b/apt-pkg/indexfile.cc @@ -8,8 +8,11 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#include <apt-pkg/configuration.h> #include <apt-pkg/indexfile.h> #include <apt-pkg/error.h> + +#include <clocale> /*}}}*/ // Global list of Item supported @@ -63,3 +66,63 @@ string pkgIndexFile::SourceInfo(pkgSrcRecords::Parser const &Record, return string(); } /*}}}*/ +// IndexFile::TranslationsAvailable - Check if will use Translation /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgIndexFile::TranslationsAvailable() +{ + const string Translation = _config->Find("APT::Acquire::Translation"); + + if (Translation.compare("none") != 0) + return CheckLanguageCode(LanguageCode().c_str()); + else + return false; +} + /*}}}*/ +// IndexFile::CheckLanguageCode - Check the Language Code /*{{{*/ +// --------------------------------------------------------------------- +/* */ +/* common cases: de_DE, de_DE@euro, de_DE.UTF-8, de_DE.UTF-8@euro, + de_DE.ISO8859-1, tig_ER + more in /etc/gdm/locale.conf +*/ + +bool pkgIndexFile::CheckLanguageCode(const char *Lang) +{ + if (strlen(Lang) == 2 || (strlen(Lang) == 5 && Lang[2] == '_')) + return true; + + if (strcmp(Lang,"C") != 0) + _error->Warning("Wrong language code %s", Lang); + + return false; +} + /*}}}*/ +// IndexFile::LanguageCode - Return the Language Code /*{{{*/ +// --------------------------------------------------------------------- +/* return the language code */ +string pkgIndexFile::LanguageCode() +{ + const string Translation = _config->Find("APT::Acquire::Translation"); + + if (Translation.compare("environment") == 0) + { + string lang = std::setlocale(LC_MESSAGES,NULL); + + // we have a mapping of the language codes that contains all the language + // codes that need the country code as well + // (like pt_BR, pt_PT, sv_SE, zh_*, en_*) + char *need_full_langcode[] = { "pt","sv","zh","en", NULL }; + for(char **s = need_full_langcode;*s != NULL; s++) + if(lang.find(*s) == 0) + return lang.substr(0,5); + + if(lang.size() > 2) + return lang.substr(0,2); + else + return lang; + } + else + return Translation; +} + /*}}}*/ diff --git a/apt-pkg/indexfile.h b/apt-pkg/indexfile.h index f1c0268b8..3cc501629 100644 --- a/apt-pkg/indexfile.h +++ b/apt-pkg/indexfile.h @@ -5,10 +5,11 @@ Index File - Abstraction for an index of archive/source file. - There are 3 primary sorts of index files, all represented by this + There are 4 primary sorts of index files, all represented by this class: Binary index files + Binary translation files Bianry index files decribing the local system Source index files @@ -77,6 +78,10 @@ class pkgIndexFile virtual bool MergeFileProvides(pkgCacheGenerator &/*Gen*/,OpProgress &/*Prog*/) const {return true;}; virtual pkgCache::PkgFileIterator FindInCache(pkgCache &Cache) const; + static bool TranslationsAvailable(); + static bool CheckLanguageCode(const char *Lang); + static string LanguageCode(); + bool IsTrusted() const { return Trusted; }; pkgIndexFile(bool Trusted): Trusted(Trusted) {}; diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc index b47378d4a..2f15486d9 100644 --- a/apt-pkg/init.cc +++ b/apt-pkg/init.cc @@ -14,6 +14,7 @@ #include <apti18n.h> #include <config.h> +#include <cstdlib> #include <sys/stat.h> /*}}}*/ @@ -40,6 +41,8 @@ bool pkgInitConfig(Configuration &Cnf) else Cnf.Set("APT::Architecture",COMMON_OS "-" COMMON_CPU); Cnf.Set("APT::Build-Essential::", "build-essential"); + Cnf.Set("APT::Install-Recommends", false); + Cnf.Set("APT::Install-Suggests", false); Cnf.Set("Dir","/"); // State @@ -102,6 +105,9 @@ bool pkgInitConfig(Configuration &Cnf) bindtextdomain(textdomain(0),Cnf.FindDir("Dir::Locale").c_str()); } #endif + + // Translation + Cnf.Set("APT::Acquire::Translation", "environment"); return true; } diff --git a/apt-pkg/init.h b/apt-pkg/init.h index 8255b406a..bc0e55036 100644 --- a/apt-pkg/init.h +++ b/apt-pkg/init.h @@ -17,8 +17,8 @@ #include <apt-pkg/pkgsystem.h> // See the makefile -#define APT_PKG_MAJOR 3 -#define APT_PKG_MINOR 11 +#define APT_PKG_MAJOR 4 +#define APT_PKG_MINOR 4 #define APT_PKG_RELEASE 0 extern const char *pkgVersion; diff --git a/apt-pkg/makefile b/apt-pkg/makefile index 7e5feae53..df9954f67 100644 --- a/apt-pkg/makefile +++ b/apt-pkg/makefile @@ -13,7 +13,7 @@ include ../buildlib/defaults.mak # methods/makefile - FIXME LIBRARY=apt-pkg LIBEXT=$(GLIBC_VER)$(LIBSTDCPP_VER) -MAJOR=3.11 +MAJOR=4.4 MINOR=0 SLIBS=$(PTHREADLIB) $(INTLLIBS) APT_DOMAIN:=libapt-pkg$(MAJOR) diff --git a/apt-pkg/orderlist.cc b/apt-pkg/orderlist.cc index 7302f02b6..616714e29 100644 --- a/apt-pkg/orderlist.cc +++ b/apt-pkg/orderlist.cc @@ -488,11 +488,13 @@ bool pkgOrderList::VisitProvides(DepIterator D,bool Critical) continue; if (D->Type != pkgCache::Dep::Conflicts && + D->Type != pkgCache::Dep::DpkgBreaks && D->Type != pkgCache::Dep::Obsoletes && Cache[Pkg].InstallVer != *I) continue; if ((D->Type == pkgCache::Dep::Conflicts || + D->Type == pkgCache::Dep::DpkgBreaks || D->Type == pkgCache::Dep::Obsoletes) && (Version *)Pkg.CurrentVer() != *I) continue; @@ -627,6 +629,7 @@ bool pkgOrderList::DepUnPackCrit(DepIterator D) /* Forward critical dependencies MUST be correct before the package can be unpacked. */ if (D->Type != pkgCache::Dep::Conflicts && + D->Type != pkgCache::Dep::DpkgBreaks && D->Type != pkgCache::Dep::Obsoletes && D->Type != pkgCache::Dep::PreDepends) continue; @@ -665,7 +668,7 @@ bool pkgOrderList::DepUnPackCrit(DepIterator D) } return true; } - /*}}}*/ + // OrderList::DepUnPackPreD - Critical UnPacking ordering with depends /*{{{*/ // --------------------------------------------------------------------- /* Critical PreDepends (also configure immediate and essential) strives to @@ -800,9 +803,20 @@ bool pkgOrderList::DepUnPackDep(DepIterator D) return false; } else + { if (D->Type == pkgCache::Dep::Depends) if (VisitProvides(D,false) == false) return false; + + if (D->Type == pkgCache::Dep::DpkgBreaks) + { + if (CheckDep(D) == true) + continue; + + if (VisitNode(D.TargetPkg()) == false) + return false; + } + } } return true; } @@ -950,6 +964,7 @@ bool pkgOrderList::CheckDep(DepIterator D) /* Conflicts requires that all versions are not present, depends just needs one */ if (D->Type != pkgCache::Dep::Conflicts && + D->Type != pkgCache::Dep::DpkgBreaks && D->Type != pkgCache::Dep::Obsoletes) { /* Try to find something that does not have the after flag set diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc index 8b94e1206..10e2858ed 100644 --- a/apt-pkg/packagemanager.cc +++ b/apt-pkg/packagemanager.cc @@ -91,9 +91,10 @@ bool pkgPackageManager::GetArchives(pkgAcquire *Owner,pkgSourceList *Sources, be downloaded. */ bool pkgPackageManager::FixMissing() { + pkgDepCache::ActionGroup group(Cache); pkgProblemResolver Resolve(&Cache); List->SetFileList(FileNames); - + bool Bad = false; for (PkgIterator I = Cache.PkgBegin(); I.end() == false; I++) { @@ -102,7 +103,7 @@ bool pkgPackageManager::FixMissing() // Okay, this file is missing and we need it. Mark it for keep Bad = true; - Cache.MarkKeep(I); + Cache.MarkKeep(I, false, false); } // We have to empty the list otherwise it will not have the new changes @@ -627,14 +628,11 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall() // --------------------------------------------------------------------- /* This uses the filenames in FileNames and the information in the DepCache to perform the installation of packages.*/ -pkgPackageManager::OrderResult pkgPackageManager::DoInstall(int status_fd) +pkgPackageManager::OrderResult pkgPackageManager::DoInstall(int statusFd) { - OrderResult Res = OrderInstall(); - if(Debug) - std::clog << "OrderInstall() returned: " << Res << std::endl; - if (Res != Failed) - if (Go(status_fd) == false) - return Failed; - return Res; + if(DoInstallPreFork() == Failed) + return Failed; + + return DoInstallPostFork(statusFd); } /*}}}*/ diff --git a/apt-pkg/packagemanager.h b/apt-pkg/packagemanager.h index 35cdca913..53600fb61 100644 --- a/apt-pkg/packagemanager.h +++ b/apt-pkg/packagemanager.h @@ -25,7 +25,9 @@ #include <string> +#include <iostream> #include <apt-pkg/pkgcache.h> +#include <apt-pkg/depcache.h> using std::string; @@ -67,13 +69,39 @@ class pkgPackageManager : protected pkgCache::Namespace virtual bool Remove(PkgIterator /*Pkg*/,bool /*Purge*/=false) {return false;}; virtual bool Go(int statusFd=-1) {return true;}; virtual void Reset() {}; - + + // the result of the operation + OrderResult Res; + public: // Main action members bool GetArchives(pkgAcquire *Owner,pkgSourceList *Sources, pkgRecords *Recs); - OrderResult DoInstall(int statusFd=-1); + + // Do the installation + OrderResult DoInstall(int statusFd=-1); + + // stuff that needs to be done before the fork() of a library that + // uses apt + OrderResult DoInstallPreFork() { + Res = OrderInstall(); + return Res; + }; + + // stuff that needs to be done after the fork + OrderResult DoInstallPostFork(int statusFd=-1) { + bool goResult = Go(statusFd); + if(goResult == false) + return Failed; + + // if all was fine update the state file + if(Res == Completed) { + Cache.writeStateFile(NULL); + } + return Res; + }; + bool FixMissing(); pkgPackageManager(pkgDepCache *Cache); diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc index 5672e60a4..7e3b9d78c 100644 --- a/apt-pkg/pkgcache.cc +++ b/apt-pkg/pkgcache.cc @@ -21,6 +21,7 @@ /*}}}*/ // Include Files /*{{{*/ #include <apt-pkg/pkgcache.h> +#include <apt-pkg/indexfile.h> #include <apt-pkg/version.h> #include <apt-pkg/error.h> #include <apt-pkg/strutl.h> @@ -38,6 +39,7 @@ using std::string; + // Cache::Header::Header - Constructor /*{{{*/ // --------------------------------------------------------------------- /* Simply initialize the header */ @@ -47,7 +49,7 @@ pkgCache::Header::Header() /* Whenever the structures change the major version should be bumped, whenever the generator changes the minor version should be bumped. */ - MajorVersion = 4; + MajorVersion = 6; MinorVersion = 0; Dirty = false; @@ -55,17 +57,22 @@ pkgCache::Header::Header() PackageSz = sizeof(pkgCache::Package); PackageFileSz = sizeof(pkgCache::PackageFile); VersionSz = sizeof(pkgCache::Version); + DescriptionSz = sizeof(pkgCache::Description); DependencySz = sizeof(pkgCache::Dependency); ProvidesSz = sizeof(pkgCache::Provides); VerFileSz = sizeof(pkgCache::VerFile); + DescFileSz = sizeof(pkgCache::DescFile); PackageCount = 0; VersionCount = 0; + DescriptionCount = 0; DependsCount = 0; PackageFileCount = 0; VerFileCount = 0; + DescFileCount = 0; ProvidesCount = 0; MaxVerFileSize = 0; + MaxDescFileSize = 0; FileList = 0; StringList = 0; @@ -84,8 +91,10 @@ bool pkgCache::Header::CheckSizes(Header &Against) const PackageSz == Against.PackageSz && PackageFileSz == Against.PackageFileSz && VersionSz == Against.VersionSz && + DescriptionSz == Against.DescriptionSz && DependencySz == Against.DependencySz && VerFileSz == Against.VerFileSz && + DescFileSz == Against.DescFileSz && ProvidesSz == Against.ProvidesSz) return true; return false; @@ -110,8 +119,10 @@ bool pkgCache::ReMap() HeaderP = (Header *)Map.Data(); PkgP = (Package *)Map.Data(); VerFileP = (VerFile *)Map.Data(); + DescFileP = (DescFile *)Map.Data(); PkgFileP = (PackageFile *)Map.Data(); VerP = (Version *)Map.Data(); + DescP = (Description *)Map.Data(); ProvideP = (Provides *)Map.Data(); DepP = (Dependency *)Map.Data(); StringItemP = (StringItem *)Map.Data(); @@ -212,8 +223,8 @@ const char *pkgCache::DepType(unsigned char Type) { const char *Types[] = {"",_("Depends"),_("PreDepends"),_("Suggests"), _("Recommends"),_("Conflicts"),_("Replaces"), - _("Obsoletes")}; - if (Type < 8) + _("Obsoletes"),_("Breaks")}; + if (Type < sizeof(Types)/sizeof(*Types)) return Types[Type]; return ""; } @@ -230,11 +241,11 @@ const char *pkgCache::Priority(unsigned char Prio) return 0; } /*}}}*/ - // Bases for iterator classes /*{{{*/ void pkgCache::VerIterator::_dummy() {} void pkgCache::DepIterator::_dummy() {} void pkgCache::PrvIterator::_dummy() {} +void pkgCache::DescIterator::_dummy() {} /*}}}*/ // PkgIterator::operator ++ - Postfix incr /*{{{*/ // --------------------------------------------------------------------- @@ -276,10 +287,11 @@ pkgCache::PkgIterator::OkState pkgCache::PkgIterator::State() const // DepIterator::IsCritical - Returns true if the dep is important /*{{{*/ // --------------------------------------------------------------------- /* Currently critical deps are defined as depends, predepends and - conflicts. */ + conflicts (including dpkg's Breaks fields). */ bool pkgCache::DepIterator::IsCritical() { if (Dep->Type == pkgCache::Dep::Conflicts || + Dep->Type == pkgCache::Dep::DpkgBreaks || Dep->Type == pkgCache::Dep::Obsoletes || Dep->Type == pkgCache::Dep::Depends || Dep->Type == pkgCache::Dep::PreDepends) @@ -365,6 +377,7 @@ pkgCache::Version **pkgCache::DepIterator::AllTargets() continue; if ((Dep->Type == pkgCache::Dep::Conflicts || + Dep->Type == pkgCache::Dep::DpkgBreaks || Dep->Type == pkgCache::Dep::Obsoletes) && ParentPkg() == I.ParentPkg()) continue; @@ -381,6 +394,7 @@ pkgCache::Version **pkgCache::DepIterator::AllTargets() continue; if ((Dep->Type == pkgCache::Dep::Conflicts || + Dep->Type == pkgCache::Dep::DpkgBreaks || Dep->Type == pkgCache::Dep::Obsoletes) && ParentPkg() == I.OwnerPkg()) continue; @@ -594,3 +608,20 @@ string pkgCache::PkgFileIterator::RelStr() return Res; } /*}}}*/ +// VerIterator::TranslatedDescription - Return the a DescIter for locale/*{{{*/ +// --------------------------------------------------------------------- +/* return a DescIter for the current locale or the default if none is + * found + */ +pkgCache::DescIterator pkgCache::VerIterator::TranslatedDescription() const +{ + pkgCache::DescIterator DescDefault = DescriptionList(); + pkgCache::DescIterator Desc = DescDefault; + for (; Desc.end() == false; Desc++) + if (pkgIndexFile::LanguageCode() == Desc.LanguageCode()) + break; + if (Desc.end() == true) Desc = DescDefault; + return Desc; +}; + + /*}}}*/ diff --git a/apt-pkg/pkgcache.h b/apt-pkg/pkgcache.h index 8e7fc252c..83b7548a3 100644 --- a/apt-pkg/pkgcache.h +++ b/apt-pkg/pkgcache.h @@ -35,32 +35,42 @@ class pkgCache struct Package; struct PackageFile; struct Version; + struct Description; struct Provides; struct Dependency; struct StringItem; struct VerFile; + struct DescFile; // Iterators class PkgIterator; class VerIterator; + class DescIterator; class DepIterator; class PrvIterator; class PkgFileIterator; class VerFileIterator; + class DescFileIterator; friend class PkgIterator; friend class VerIterator; + friend class DescInterator; friend class DepIterator; friend class PrvIterator; friend class PkgFileIterator; friend class VerFileIterator; + friend class DescFileIterator; class Namespace; // These are all the constants used in the cache structures + + // WARNING - if you change these lists you must also edit + // the stringification in pkgcache.cc and also consider whether + // the cache file will become incompatible. struct Dep { enum DepType {Depends=1,PreDepends=2,Suggests=3,Recommends=4, - Conflicts=5,Replaces=6,Obsoletes=7}; + Conflicts=5,Replaces=6,Obsoletes=7,DpkgBreaks=8}; enum DepCompareOp {Or=0x10,NoOp=0,LessEq=0x1,GreaterEq=0x2,Less=0x3, Greater=0x4,Equals=0x5,NotEquals=0x6}; }; @@ -95,8 +105,10 @@ class pkgCache Header *HeaderP; Package *PkgP; VerFile *VerFileP; + DescFile *DescFileP; PackageFile *PkgFileP; Version *VerP; + Description *DescP; Provides *ProvideP; Dependency *DepP; StringItem *StringItemP; @@ -148,16 +160,20 @@ struct pkgCache::Header unsigned short PackageSz; unsigned short PackageFileSz; unsigned short VersionSz; + unsigned short DescriptionSz; unsigned short DependencySz; unsigned short ProvidesSz; unsigned short VerFileSz; + unsigned short DescFileSz; // Structure counts unsigned long PackageCount; unsigned long VersionCount; + unsigned long DescriptionCount; unsigned long DependsCount; unsigned long PackageFileCount; unsigned long VerFileCount; + unsigned long DescFileCount; unsigned long ProvidesCount; // Offsets @@ -166,10 +182,11 @@ struct pkgCache::Header map_ptrloc VerSysName; // StringTable map_ptrloc Architecture; // StringTable unsigned long MaxVerFileSize; + unsigned long MaxDescFileSize; /* Allocation pools, there should be one of these for each structure excluding the header */ - DynamicMMap::Pool Pools[7]; + DynamicMMap::Pool Pools[8]; // Rapid package name lookup map_ptrloc HashTable[2*1048]; @@ -190,7 +207,7 @@ struct pkgCache::Package map_ptrloc NextPackage; // Package map_ptrloc RevDepends; // Dependency map_ptrloc ProvidesList; // Provides - + // Install/Remove/Purge etc unsigned char SelectedState; // What unsigned char InstState; // Flags @@ -229,6 +246,14 @@ struct pkgCache::VerFile unsigned short Size; }; +struct pkgCache::DescFile +{ + map_ptrloc File; // PackageFile + map_ptrloc NextFile; // PkgVerFile + map_ptrloc Offset; // File offset + unsigned short Size; +}; + struct pkgCache::Version { map_ptrloc VerStr; // Stringtable @@ -238,6 +263,7 @@ struct pkgCache::Version // Lists map_ptrloc FileList; // VerFile map_ptrloc NextVer; // Version + map_ptrloc DescriptionList; // Description map_ptrloc DependsList; // Dependency map_ptrloc ParentPkg; // Package map_ptrloc ProvidesList; // Provides @@ -249,6 +275,22 @@ struct pkgCache::Version unsigned char Priority; }; +struct pkgCache::Description +{ + // Language Code store the description translation language code. If + // the value has a 0 lenght then this is readed using the Package + // file else the Translation-CODE are used. + map_ptrloc language_code; // StringTable + map_ptrloc md5sum; // StringTable + + // Linked list + map_ptrloc FileList; // DescFile + map_ptrloc NextDesc; // Description + map_ptrloc ParentPkg; // Package + + unsigned short ID; +}; + struct pkgCache::Dependency { map_ptrloc Version; // Stringtable @@ -296,11 +338,13 @@ class pkgCache::Namespace typedef pkgCache::PkgIterator PkgIterator; typedef pkgCache::VerIterator VerIterator; + typedef pkgCache::DescIterator DescIterator; typedef pkgCache::DepIterator DepIterator; typedef pkgCache::PrvIterator PrvIterator; typedef pkgCache::PkgFileIterator PkgFileIterator; typedef pkgCache::VerFileIterator VerFileIterator; typedef pkgCache::Version Version; + typedef pkgCache::Description Description; typedef pkgCache::Package Package; typedef pkgCache::Header Header; typedef pkgCache::Dep Dep; diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc index 1c93b32a3..ce9b96980 100644 --- a/apt-pkg/pkgcachegen.cc +++ b/apt-pkg/pkgcachegen.cc @@ -22,6 +22,8 @@ #include <apt-pkg/sptr.h> #include <apt-pkg/pkgsystem.h> +#include <apt-pkg/tagfile.h> + #include <apti18n.h> #include <vector> @@ -121,16 +123,46 @@ bool pkgCacheGenerator::MergeList(ListParser &List, string Version = List.Version(); if (Version.empty() == true) { + // we first process the package, then the descriptions + // (this has the bonus that we get MMap error when we run out + // of MMap space) if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false) return _error->Error(_("Error occurred while processing %s (UsePackage1)"), PackageName.c_str()); + + // Find the right version to write the description + MD5SumValue CurMd5 = List.Description_md5(); + pkgCache::VerIterator Ver = Pkg.VersionList(); + map_ptrloc *LastVer = &Pkg->VersionList; + + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) + { + pkgCache::DescIterator Desc = Ver.DescriptionList(); + map_ptrloc *LastDesc = &Ver->DescriptionList; + + for (; Desc.end() == false; LastDesc = &Desc->NextDesc, Desc++) + { + + if (MD5SumValue(Desc.md5()) == CurMd5) + { + // Add new description + *LastDesc = NewDescription(Desc, List.DescriptionLanguage(), CurMd5, *LastDesc); + Desc->ParentPkg = Pkg.Index(); + + if (NewFileDesc(Desc,List) == false) + return _error->Error(_("Error occured while processing %s (NewFileDesc1)"),PackageName.c_str()); + break; + } + } + } + continue; } pkgCache::VerIterator Ver = Pkg.VersionList(); - map_ptrloc *Last = &Pkg->VersionList; + map_ptrloc *LastVer = &Pkg->VersionList; int Res = 1; - for (; Ver.end() == false; Last = &Ver->NextVer, Ver++) + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) { Res = Cache.VS->CmpVersion(Version,Ver.VerStr()); if (Res >= 0) @@ -164,7 +196,7 @@ bool pkgCacheGenerator::MergeList(ListParser &List, // Skip to the end of the same version set. if (Res == 0) { - for (; Ver.end() == false; Last = &Ver->NextVer, Ver++) + for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++) { Res = Cache.VS->CmpVersion(Version,Ver.VerStr()); if (Res != 0) @@ -173,9 +205,10 @@ bool pkgCacheGenerator::MergeList(ListParser &List, } // Add a new version - *Last = NewVersion(Ver,Version,*Last); + *LastVer = NewVersion(Ver,Version,*LastVer); Ver->ParentPkg = Pkg.Index(); Ver->Hash = Hash; + if (List.NewVersion(Ver) == false) return _error->Error(_("Error occurred while processing %s (NewVersion1)"), PackageName.c_str()); @@ -195,6 +228,21 @@ bool pkgCacheGenerator::MergeList(ListParser &List, FoundFileDeps |= List.HasFileDeps(); return true; } + + /* Record the Description data. Description data always exist in + Packages and Translation-* files. */ + pkgCache::DescIterator Desc = Ver.DescriptionList(); + map_ptrloc *LastDesc = &Ver->DescriptionList; + + // Skip to the end of description set + for (; Desc.end() == false; LastDesc = &Desc->NextDesc, Desc++); + + // Add new description + *LastDesc = NewDescription(Desc, List.DescriptionLanguage(), List.Description_md5(), *LastDesc); + Desc->ParentPkg = Pkg.Index(); + + if (NewFileDesc(Desc,List) == false) + return _error->Error(_("Error occured while processing %s (NewFileDesc2)"),PackageName.c_str()); } FoundFileDeps |= List.HasFileDeps(); @@ -205,6 +253,9 @@ bool pkgCacheGenerator::MergeList(ListParser &List, if (Cache.HeaderP->VersionCount >= (1ULL<<(sizeof(Cache.VerP->ID)*8))-1) return _error->Error(_("Wow, you exceeded the number of versions " "this APT is capable of.")); + if (Cache.HeaderP->DescriptionCount >= (1ULL<<(sizeof(Cache.DescP->ID)*8))-1) + return _error->Error(_("Wow, you exceeded the number of descriptions " + "this APT is capable of.")); if (Cache.HeaderP->DependsCount >= (1ULL<<(sizeof(Cache.DepP->ID)*8))-1ULL) return _error->Error(_("Wow, you exceeded the number of dependencies " "this APT is capable of.")); @@ -267,7 +318,7 @@ bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,const string &Name Pkg = Cache.FindPkg(Name); if (Pkg.end() == false) return true; - + // Get a structure unsigned long Package = Map.Allocate(sizeof(pkgCache::Package)); if (Package == 0) @@ -345,6 +396,62 @@ unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver, return Version; } /*}}}*/ +// CacheGenerator::NewFileDesc - Create a new File<->Desc association /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool pkgCacheGenerator::NewFileDesc(pkgCache::DescIterator &Desc, + ListParser &List) +{ + if (CurrentFile == 0) + return true; + + // Get a structure + unsigned long DescFile = Map.Allocate(sizeof(pkgCache::DescFile)); + if (DescFile == 0) + return 0; + + pkgCache::DescFileIterator DF(Cache,Cache.DescFileP + DescFile); + DF->File = CurrentFile - Cache.PkgFileP; + + // Link it to the end of the list + map_ptrloc *Last = &Desc->FileList; + for (pkgCache::DescFileIterator D = Desc.FileList(); D.end() == false; D++) + Last = &D->NextFile; + + DF->NextFile = *Last; + *Last = DF.Index(); + + DF->Offset = List.Offset(); + DF->Size = List.Size(); + if (Cache.HeaderP->MaxDescFileSize < DF->Size) + Cache.HeaderP->MaxDescFileSize = DF->Size; + Cache.HeaderP->DescFileCount++; + + return true; +} + /*}}}*/ +// CacheGenerator::NewDescription - Create a new Description /*{{{*/ +// --------------------------------------------------------------------- +/* This puts a description structure in the linked list */ +map_ptrloc pkgCacheGenerator::NewDescription(pkgCache::DescIterator &Desc, + const string &Lang, const MD5SumValue &md5sum, + map_ptrloc Next) +{ + // Get a structure + map_ptrloc Description = Map.Allocate(sizeof(pkgCache::Description)); + if (Description == 0) + return 0; + + // Fill it in + Desc = pkgCache::DescIterator(Cache,Cache.DescP + Description); + Desc->NextDesc = Next; + Desc->ID = Cache.HeaderP->DescriptionCount++; + Desc->language_code = Map.WriteString(Lang); + Desc->md5sum = Map.WriteString(md5sum.Value()); + + return Description; +} + /*}}}*/ // ListParser::NewDepends - Create a dependency element /*{{{*/ // --------------------------------------------------------------------- /* This creates a dependency element in the tree. It is linked to the @@ -567,8 +674,10 @@ static bool CheckValidity(const string &CacheFile, FileIterator Start, if ((*Start)->Exists() == false) { +#if 0 // mvo: we no longer give a message here (Default Sources spec) _error->WarningE("stat",_("Couldn't stat source package list %s"), (*Start)->Describe().c_str()); +#endif continue; } @@ -576,7 +685,7 @@ static bool CheckValidity(const string &CacheFile, FileIterator Start, pkgCache::PkgFileIterator File = (*Start)->FindInCache(Cache); if (File.end() == true) return false; - + Visited[File->ID] = true; } @@ -672,7 +781,7 @@ static bool BuildCache(pkgCacheGenerator &Gen, bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress, MMap **OutMap,bool AllowMem) { - unsigned long MapSize = _config->FindI("APT::Cache-Limit",12*1024*1024); + unsigned long MapSize = _config->FindI("APT::Cache-Limit",16*1024*1024); vector<pkgIndexFile *> Files; for (vector<metaIndex *>::const_iterator i = List.begin(); @@ -819,7 +928,7 @@ bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress, /* */ bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap) { - unsigned long MapSize = _config->FindI("APT::Cache-Limit",8*1024*1024); + unsigned long MapSize = _config->FindI("APT::Cache-Limit",12*1024*1024); vector<pkgIndexFile *> Files; unsigned long EndOfSource = Files.size(); if (_system->AddStatusFiles(Files) == false) diff --git a/apt-pkg/pkgcachegen.h b/apt-pkg/pkgcachegen.h index 70616c0a8..a47b097b3 100644 --- a/apt-pkg/pkgcachegen.h +++ b/apt-pkg/pkgcachegen.h @@ -21,6 +21,7 @@ #include <apt-pkg/pkgcache.h> +#include <apt-pkg/md5.h> class pkgSourceList; class OpProgress; @@ -52,7 +53,9 @@ class pkgCacheGenerator bool NewPackage(pkgCache::PkgIterator &Pkg,const string &Pkg); bool NewFileVer(pkgCache::VerIterator &Ver,ListParser &List); + bool NewFileDesc(pkgCache::DescIterator &Desc,ListParser &List); unsigned long NewVersion(pkgCache::VerIterator &Ver,const string &VerStr,unsigned long Next); + map_ptrloc NewDescription(pkgCache::DescIterator &Desc,const string &Lang,const MD5SumValue &md5sum,map_ptrloc Next); public: @@ -105,6 +108,9 @@ class pkgCacheGenerator::ListParser virtual string Package() = 0; virtual string Version() = 0; virtual bool NewVersion(pkgCache::VerIterator Ver) = 0; + virtual string Description() = 0; + virtual string DescriptionLanguage() = 0; + virtual MD5SumValue Description_md5() = 0; virtual unsigned short VersionHash() = 0; virtual bool UsePackage(pkgCache::PkgIterator Pkg, pkgCache::VerIterator Ver) = 0; diff --git a/apt-pkg/pkgrecords.cc b/apt-pkg/pkgrecords.cc index dc43735a6..0aaa2096a 100644 --- a/apt-pkg/pkgrecords.cc +++ b/apt-pkg/pkgrecords.cc @@ -60,3 +60,12 @@ pkgRecords::Parser &pkgRecords::Lookup(pkgCache::VerFileIterator const &Ver) return *Files[Ver.File()->ID]; } /*}}}*/ +// Records::Lookup - Get a parser for the package description file /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgRecords::Parser &pkgRecords::Lookup(pkgCache::DescFileIterator const &Desc) +{ + Files[Desc.File()->ID]->Jump(Desc); + return *Files[Desc.File()->ID]; +} + /*}}}*/ diff --git a/apt-pkg/pkgrecords.h b/apt-pkg/pkgrecords.h index d431101fe..ea1a23fc4 100644 --- a/apt-pkg/pkgrecords.h +++ b/apt-pkg/pkgrecords.h @@ -35,6 +35,7 @@ class pkgRecords // Lookup function Parser &Lookup(pkgCache::VerFileIterator const &Ver); + Parser &Lookup(pkgCache::DescFileIterator const &Desc); // Construct destruct pkgRecords(pkgCache &Cache); @@ -46,6 +47,7 @@ class pkgRecords::Parser protected: virtual bool Jump(pkgCache::VerFileIterator const &Ver) = 0; + virtual bool Jump(pkgCache::DescFileIterator const &Desc) = 0; public: friend class pkgRecords; @@ -55,6 +57,7 @@ class pkgRecords::Parser virtual string MD5Hash() {return string();}; virtual string SHA1Hash() {return string();}; virtual string SourcePkg() {return string();}; + virtual string SourceVer() {return string();}; // These are some general stats about the package virtual string Maintainer() {return string();}; diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc index 795f1d787..8b083fd44 100644 --- a/apt-pkg/policy.cc +++ b/apt-pkg/policy.cc @@ -33,6 +33,7 @@ #include <apti18n.h> #include <iostream> +#include <sstream> /*}}}*/ using namespace std; @@ -297,7 +298,13 @@ bool ReadPinFile(pkgPolicy &Plcy,string File) continue; } - Plcy.CreatePin(Type,Name,string(Word,End),priority); + istringstream s(Name); + string pkg; + while(!s.eof()) + { + s >> pkg; + Plcy.CreatePin(Type, pkg, string(Word,End),priority); + }; } Plcy.InitDefaults(); diff --git a/apt-pkg/tagfile.cc b/apt-pkg/tagfile.cc index 85e6abfd7..893cb8ee7 100644 --- a/apt-pkg/tagfile.cc +++ b/apt-pkg/tagfile.cc @@ -419,6 +419,7 @@ static const char *iTFRewritePackageOrder[] = { "Recommends", "Suggests", "Conflicts", + "Breaks", "Conffiles", "Filename", "Size", diff --git a/apt-pkg/vendorlist.cc b/apt-pkg/vendorlist.cc index 72694dd75..8e5d09e8a 100644 --- a/apt-pkg/vendorlist.cc +++ b/apt-pkg/vendorlist.cc @@ -113,7 +113,7 @@ bool pkgVendorList::CreateList(Configuration& Cnf) const Vendor* pkgVendorList::LookupFingerprint(string Fingerprint) { - for (const_iterator I = begin(); I != end(); ++I) + for (const_iterator I = VendorList.begin(); I != VendorList.end(); ++I) { if ((*I)->LookupFingerprint(Fingerprint) != "") return *I; |