- enable self-referencing provides and see what breaks...
[apt.git] / apt-pkg / pkgcachegen.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: pkgcachegen.cc,v 1.53 2003/02/02 02:44:20 doogie Exp $
4 /* ######################################################################
5    
6    Package Cache Generator - Generator for the cache structure.
7    
8    This builds the cache structure from the abstract package list parser. 
9    
10    ##################################################################### */
11                                                                         /*}}}*/
12 // Include Files                                                        /*{{{*/
13 #ifdef __GNUG__
14 #pragma implementation "apt-pkg/pkgcachegen.h"
15 #endif
16
17 #define APT_COMPATIBILITY 986
18
19 #include <apt-pkg/pkgcachegen.h>
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/version.h>
22 #include <apt-pkg/progress.h>
23 #include <apt-pkg/sourcelist.h>
24 #include <apt-pkg/configuration.h>
25 #include <apt-pkg/strutl.h>
26 #include <apt-pkg/sptr.h>
27 #include <apt-pkg/pkgsystem.h>
28
29 #include <apti18n.h>
30
31 #include <vector>
32
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <system.h>
38                                                                         /*}}}*/
39 typedef vector<pkgIndexFile *>::iterator FileIterator;
40
41 // CacheGenerator::pkgCacheGenerator - Constructor                      /*{{{*/
42 // ---------------------------------------------------------------------
43 /* We set the diry flag and make sure that is written to the disk */
44 pkgCacheGenerator::pkgCacheGenerator(DynamicMMap *pMap,OpProgress *Prog) :
45                     Map(*pMap), Cache(pMap,false), Progress(Prog),
46                     FoundFileDeps(0)
47 {
48    CurrentFile = 0;
49    memset(UniqHash,0,sizeof(UniqHash));
50    
51    if (_error->PendingError() == true)
52       return;
53
54    if (Map.Size() == 0)
55    {
56       // Setup the map interface..
57       Cache.HeaderP = (pkgCache::Header *)Map.Data();
58       Map.RawAllocate(sizeof(pkgCache::Header));
59       Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
60       
61       // Starting header
62       *Cache.HeaderP = pkgCache::Header();
63       Cache.HeaderP->VerSysName = Map.WriteString(_system->VS->Label);
64       Cache.HeaderP->Architecture = Map.WriteString(_config->Find("APT::Architecture"));
65       Cache.ReMap(); 
66    }
67    else
68    {
69       // Map directly from the existing file
70       Cache.ReMap(); 
71       Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
72       if (Cache.VS != _system->VS)
73       {
74          _error->Error(_("Cache has an incompatible versioning system"));
75          return;
76       }      
77    }
78    
79    Cache.HeaderP->Dirty = true;
80    Map.Sync(0,sizeof(pkgCache::Header));
81 }
82                                                                         /*}}}*/
83 // CacheGenerator::~pkgCacheGenerator - Destructor                      /*{{{*/
84 // ---------------------------------------------------------------------
85 /* We sync the data then unset the dirty flag in two steps so as to
86    advoid a problem during a crash */
87 pkgCacheGenerator::~pkgCacheGenerator()
88 {
89    if (_error->PendingError() == true)
90       return;
91    if (Map.Sync() == false)
92       return;
93    
94    Cache.HeaderP->Dirty = false;
95    Map.Sync(0,sizeof(pkgCache::Header));
96 }
97                                                                         /*}}}*/
98 // CacheGenerator::MergeList - Merge the package list                   /*{{{*/
99 // ---------------------------------------------------------------------
100 /* This provides the generation of the entries in the cache. Each loop
101    goes through a single package record from the underlying parse engine. */
102 bool pkgCacheGenerator::MergeList(ListParser &List,
103                                   pkgCache::VerIterator *OutVer)
104 {
105    List.Owner = this;
106
107    // CNC:2003-02-20 - When --reinstall is used during a cache building
108    //                  process, the algorithm is sligthly changed to
109    //                  order the "better" architectures before, even if
110    //                  they are already in the system.
111    bool ReInstall = _config->FindB("APT::Get::ReInstall", false);
112
113    unsigned int Counter = 0;
114    while (List.Step() == true)
115    {
116       // Get a pointer to the package structure
117       string PackageName = List.Package();
118       if (PackageName.empty() == true)
119          return false;
120       
121       pkgCache::PkgIterator Pkg;
122       if (NewPackage(Pkg,PackageName) == false)
123          return _error->Error(_("Error occured while processing %s (NewPackage)"),PackageName.c_str());
124       Counter++;
125       // CNC:2003-02-16
126       if (Counter % 100 == 0 && Progress != 0) {
127          if (List.OrderedOffset() == true)
128             Progress->Progress(List.Offset());
129          else
130             Progress->Progress(Counter);
131       }
132
133       /* Get a pointer to the version structure. We know the list is sorted
134          so we use that fact in the search. Insertion of new versions is
135          done with correct sorting */
136       string Version = List.Version();
137       if (Version.empty() == true)
138       {
139          if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false)
140             return _error->Error(_("Error occured while processing %s (UsePackage1)"),
141                                  PackageName.c_str());
142          continue;
143       }
144
145       // CNC:2002-07-09
146       string Arch = List.Architecture();
147
148       pkgCache::VerIterator Ver = Pkg.VersionList();
149       map_ptrloc *Last = &Pkg->VersionList;
150       int Res = 1;
151       for (; Ver.end() == false; Last = &Ver->NextVer, Ver++)
152       {
153          // 2003-02-20 - If the package is already installed, the
154          //              architecture doesn't matter, unless
155          //              --reinstall has been used.
156          if (!ReInstall && List.IsDatabase())
157             Res = Cache.VS->CmpVersion(Version, Ver.VerStr());
158          else
159             Res = Cache.VS->CmpVersionArch(Version,Arch,
160                                            Ver.VerStr(),Ver.Arch());
161          if (Res >= 0)
162             break;
163       }
164       
165       /* We already have a version for this item, record that we
166          saw it */
167       unsigned long Hash = List.VersionHash();
168       if (Res == 0 && Ver->Hash == Hash)
169       {
170          if (List.UsePackage(Pkg,Ver) == false)
171             return _error->Error(_("Error occured while processing %s (UsePackage2)"),
172                                  PackageName.c_str());
173
174          if (NewFileVer(Ver,List) == false)
175             return _error->Error(_("Error occured while processing %s (NewFileVer1)"),
176                                  PackageName.c_str());
177          
178          // Read only a single record and return
179          if (OutVer != 0)
180          {
181             *OutVer = Ver;
182             FoundFileDeps |= List.HasFileDeps();
183             return true;
184          }
185          
186          continue;
187       }      
188
189       // Skip to the end of the same version set.
190       if (Res == 0)
191       {
192          // CNC:2003-02-20 - Unless this package is already installed.
193          if (!List.IsDatabase())
194          for (; Ver.end() == false; Last = &Ver->NextVer, Ver++)
195          {
196             // CNC:2002-07-09
197             Res = Cache.VS->CmpVersionArch(Version,Arch,
198                                            Ver.VerStr(),Ver.Arch());
199             if (Res != 0)
200                break;
201          }
202       }
203
204       // Add a new version
205       *Last = NewVersion(Ver,Version,*Last);
206       Ver->ParentPkg = Pkg.Index();
207       Ver->Hash = Hash;
208       if (List.NewVersion(Ver) == false)
209          return _error->Error(_("Error occured while processing %s (NewVersion1)"),
210                               PackageName.c_str());
211
212       if (List.UsePackage(Pkg,Ver) == false)
213          return _error->Error(_("Error occured while processing %s (UsePackage3)"),
214                               PackageName.c_str());
215       
216       if (NewFileVer(Ver,List) == false)
217          return _error->Error(_("Error occured while processing %s (NewVersion2)"),
218                               PackageName.c_str());
219
220       // Read only a single record and return
221       if (OutVer != 0)
222       {
223          *OutVer = Ver;
224          FoundFileDeps |= List.HasFileDeps();
225          return true;
226       }      
227    }
228
229    FoundFileDeps |= List.HasFileDeps();
230
231    if (Cache.HeaderP->PackageCount >= (1ULL<<sizeof(Cache.PkgP->ID)*8)-1)
232       return _error->Error(_("Wow, you exceeded the number of package "
233                              "names this APT is capable of."));
234    if (Cache.HeaderP->VersionCount >= (1ULL<<(sizeof(Cache.VerP->ID)*8))-1)
235       return _error->Error(_("Wow, you exceeded the number of versions "
236                              "this APT is capable of."));
237    if (Cache.HeaderP->DependsCount >= (1ULL<<(sizeof(Cache.DepP->ID)*8))-1ULL)
238       return _error->Error(_("Wow, you exceeded the number of dependencies "
239                              "this APT is capable of."));
240    return true;
241 }
242                                                                         /*}}}*/
243 // CacheGenerator::MergeFileProvides - Merge file provides              /*{{{*/
244 // ---------------------------------------------------------------------
245 /* If we found any file depends while parsing the main list we need to 
246    resolve them. Since it is undesired to load the entire list of files
247    into the cache as virtual packages we do a two stage effort. MergeList
248    identifies the file depends and this creates Provdies for them by
249    re-parsing all the indexs. */
250 bool pkgCacheGenerator::MergeFileProvides(ListParser &List)
251 {
252    List.Owner = this;
253    
254    unsigned int Counter = 0;
255    while (List.Step() == true)
256    {
257       string PackageName = List.Package();
258       if (PackageName.empty() == true)
259          return false;
260       string Version = List.Version();
261       if (Version.empty() == true)
262          continue;
263       
264       pkgCache::PkgIterator Pkg = Cache.FindPkg(PackageName);
265       if (Pkg.end() == true)
266 #if 0
267          // CNC:2003-03-03 - Ignore missing packages. This will happen when
268          //                  a package is placed in Allow-Duplicated and
269          //                  then removed, but the source cache is still
270          //                  counting with it as Allow-Duplicated. No good
271          //                  way to handle that right now.
272          return _error->Error(_("Error occured while processing %s (FindPkg)"),
273                                 PackageName.c_str());
274 #else
275          continue;
276 #endif
277
278       Counter++;
279       // CNC:2003-02-16
280       if (Counter % 100 == 0 && Progress != 0) {
281          if (List.OrderedOffset() == true)
282             Progress->Progress(List.Offset());
283          else
284             Progress->Progress(Counter);
285       }
286
287       unsigned long Hash = List.VersionHash();
288       pkgCache::VerIterator Ver = Pkg.VersionList();
289       for (; Ver.end() == false; Ver++)
290       {
291          // CNC:2002-07-25
292          if (Ver->Hash == Hash && strcmp(Version.c_str(), Ver.VerStr()) == 0)
293          {
294             if (List.CollectFileProvides(Cache,Ver) == false)
295                return _error->Error(_("Error occured while processing %s (CollectFileProvides)"),PackageName.c_str());
296             break;
297          }
298       }
299       
300       // CNC:2003-03-03 - Ignore missing versions. This will happen when
301       //                  a package is placed in Allow-Duplicated and
302       //                  then removed, but the source cache is still
303       //                  counting with it as Allow-Duplicated. No good
304       //                  way to handle that right now.
305 #if 0
306       if (Ver.end() == true)
307          _error->Warning(_("Package %s %s was not found while processing file dependencies"),PackageName.c_str(),Version.c_str());
308 #endif
309    }
310
311    return true;
312 }
313                                                                         /*}}}*/
314 // CacheGenerator::NewPackage - Add a new package                       /*{{{*/
315 // ---------------------------------------------------------------------
316 /* This creates a new package structure and adds it to the hash table */
317 bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,string Name)
318 {
319 // CNC:2003-02-17 - Optimized.
320 #if 0
321    Pkg = Cache.FindPkg(Name);
322    if (Pkg.end() == false)
323       return true;
324 #else
325    pkgCache::Package *P = Cache.FindPackage(Name.c_str());
326    if (P != NULL) {
327       Pkg = pkgCache::PkgIterator(Cache, P);
328       return true;
329    }
330 #endif
331        
332    // Get a structure
333    unsigned long Package = Map.Allocate(sizeof(pkgCache::Package));
334    if (Package == 0)
335       return false;
336    
337    Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package);
338    
339    // Insert it into the hash table
340    unsigned long Hash = Cache.Hash(Name);
341    Pkg->NextPackage = Cache.HeaderP->HashTable[Hash];
342    Cache.HeaderP->HashTable[Hash] = Package;
343    
344    // Set the name and the ID
345    Pkg->Name = Map.WriteString(Name);
346    if (Pkg->Name == 0)
347       return false;
348    Pkg->ID = Cache.HeaderP->PackageCount++;
349    
350    return true;
351 }
352                                                                         /*}}}*/
353 // CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
354 // ---------------------------------------------------------------------
355 /* */
356 bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver,
357                                    ListParser &List)
358 {
359    if (CurrentFile == 0)
360       return true;
361    
362    // Get a structure
363    unsigned long VerFile = Map.Allocate(sizeof(pkgCache::VerFile));
364    if (VerFile == 0)
365       return 0;
366    
367    pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile);
368    VF->File = CurrentFile - Cache.PkgFileP;
369    
370    // Link it to the end of the list
371    map_ptrloc *Last = &Ver->FileList;
372    for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; V++)
373       Last = &V->NextFile;
374    VF->NextFile = *Last;
375    *Last = VF.Index();
376    
377    VF->Offset = List.Offset();
378    VF->Size = List.Size();
379    if (Cache.HeaderP->MaxVerFileSize < VF->Size)
380       Cache.HeaderP->MaxVerFileSize = VF->Size;
381    Cache.HeaderP->VerFileCount++;
382    
383    return true;
384 }
385                                                                         /*}}}*/
386 // CacheGenerator::NewVersion - Create a new Version                    /*{{{*/
387 // ---------------------------------------------------------------------
388 /* This puts a version structure in the linked list */
389 unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver,
390                                             string VerStr,
391                                             unsigned long Next)
392 {
393    // Get a structure
394    unsigned long Version = Map.Allocate(sizeof(pkgCache::Version));
395    if (Version == 0)
396       return 0;
397    
398    // Fill it in
399    Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
400    Ver->NextVer = Next;
401    Ver->ID = Cache.HeaderP->VersionCount++;
402    Ver->VerStr = Map.WriteString(VerStr);
403    if (Ver->VerStr == 0)
404       return 0;
405    
406    return Version;
407 }
408                                                                         /*}}}*/
409 // ListParser::NewDepends - Create a dependency element                 /*{{{*/
410 // ---------------------------------------------------------------------
411 /* This creates a dependency element in the tree. It is linked to the
412    version and to the package that it is pointing to. */
413 bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver,
414                                                string PackageName,
415                                                string Version,
416                                                unsigned int Op,
417                                                unsigned int Type)
418 {
419    pkgCache &Cache = Owner->Cache;
420    
421    // Get a structure
422    unsigned long Dependency = Owner->Map.Allocate(sizeof(pkgCache::Dependency));
423    if (Dependency == 0)
424       return false;
425    
426    // Fill it in
427    pkgCache::DepIterator Dep(Cache,Cache.DepP + Dependency);
428    Dep->ParentVer = Ver.Index();
429    Dep->Type = Type;
430    Dep->CompareOp = Op;
431    Dep->ID = Cache.HeaderP->DependsCount++;
432    
433    // Locate the target package
434    pkgCache::PkgIterator Pkg;
435    if (Owner->NewPackage(Pkg,PackageName) == false)
436       return false;
437    
438    // Probe the reverse dependency list for a version string that matches
439    if (Version.empty() == false)
440    {
441 /*      for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++)
442          if (I->Version != 0 && I.TargetVer() == Version)
443             Dep->Version = I->Version;*/
444       if (Dep->Version == 0)
445          if ((Dep->Version = WriteString(Version)) == 0)
446             return false;
447    }
448       
449    // Link it to the package
450    Dep->Package = Pkg.Index();
451    Dep->NextRevDepends = Pkg->RevDepends;
452    Pkg->RevDepends = Dep.Index();
453    
454    /* Link it to the version (at the end of the list)
455       Caching the old end point speeds up generation substantially */
456    if (OldDepVer != Ver)
457    {
458       OldDepLast = &Ver->DependsList;
459       for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
460          OldDepLast = &D->NextDepends;
461       OldDepVer = Ver;
462    }
463
464    // Is it a file dependency?
465    if (PackageName[0] == '/')
466       FoundFileDeps = true;
467    
468    Dep->NextDepends = *OldDepLast;
469    *OldDepLast = Dep.Index();
470    OldDepLast = &Dep->NextDepends;
471
472    return true;
473 }
474                                                                         /*}}}*/
475 // ListParser::NewProvides - Create a Provides element                  /*{{{*/
476 // ---------------------------------------------------------------------
477 /* */
478 bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver,
479                                                 string PackageName,
480                                                 string Version)
481 {
482    pkgCache &Cache = Owner->Cache;
483
484 // PM:2006-02-07 allow self-referencing provides for now at least...
485 #if 0
486    // We do not add self referencing provides
487    if (Ver.ParentPkg().Name() == PackageName)
488       return true;
489 #endif
490    
491    // Get a structure
492    unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides));
493    if (Provides == 0)
494       return false;
495    Cache.HeaderP->ProvidesCount++;
496    
497    // Fill it in
498    pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
499    Prv->Version = Ver.Index();
500    Prv->NextPkgProv = Ver->ProvidesList;
501    Ver->ProvidesList = Prv.Index();
502    if (Version.empty() == false && (Prv->ProvideVersion = WriteString(Version)) == 0)
503       return false;
504    
505    // Locate the target package
506    pkgCache::PkgIterator Pkg;
507    if (Owner->NewPackage(Pkg,PackageName) == false)
508       return false;
509    
510    // Link it to the package
511    Prv->ParentPkg = Pkg.Index();
512    Prv->NextProvides = Pkg->ProvidesList;
513    Pkg->ProvidesList = Prv.Index();
514    
515    return true;
516 }
517                                                                         /*}}}*/
518 // CacheGenerator::SelectFile - Select the current file being parsed    /*{{{*/
519 // ---------------------------------------------------------------------
520 /* This is used to select which file is to be associated with all newly
521    added versions. The caller is responsible for setting the IMS fields. */
522 bool pkgCacheGenerator::SelectFile(string File,string Site,
523                                    const pkgIndexFile &Index,
524                                    unsigned long Flags)
525 {
526    // Get some space for the structure
527    CurrentFile = Cache.PkgFileP + Map.Allocate(sizeof(*CurrentFile));
528    if (CurrentFile == Cache.PkgFileP)
529       return false;
530    
531    // Fill it in
532    CurrentFile->FileName = Map.WriteString(File);
533    CurrentFile->Site = WriteUniqString(Site);
534    CurrentFile->NextFile = Cache.HeaderP->FileList;
535    CurrentFile->Flags = Flags;
536    CurrentFile->ID = Cache.HeaderP->PackageFileCount;
537    CurrentFile->IndexType = WriteUniqString(Index.GetType()->Label);
538    PkgFileName = File;
539    Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
540    Cache.HeaderP->PackageFileCount++;
541
542    if (CurrentFile->FileName == 0)
543       return false;
544    
545    if (Progress != 0)
546       Progress->SubProgress(Index.Size());
547    return true;
548 }
549                                                                         /*}}}*/
550 // CacheGenerator::WriteUniqueString - Insert a unique string           /*{{{*/
551 // ---------------------------------------------------------------------
552 /* This is used to create handles to strings. Given the same text it
553    always returns the same number */
554 unsigned long pkgCacheGenerator::WriteUniqString(const char *S,
555                                                  unsigned int Size)
556 {
557    /* We use a very small transient hash table here, this speeds up generation
558       by a fair amount on slower machines */
559    pkgCache::StringItem *&Bucket = UniqHash[(S[0]*5 + S[1]) % _count(UniqHash)];
560    if (Bucket != 0 && 
561        stringcmp(S,S+Size,Cache.StrP + Bucket->String) == 0)
562       return Bucket->String;
563    
564    // Search for an insertion point
565    pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList;
566    int Res = 1;
567    map_ptrloc *Last = &Cache.HeaderP->StringList;
568    for (; I != Cache.StringItemP; Last = &I->NextItem, 
569         I = Cache.StringItemP + I->NextItem)
570    {
571       Res = stringcmp(S,S+Size,Cache.StrP + I->String);
572       if (Res >= 0)
573          break;
574    }
575    
576    // Match
577    if (Res == 0)
578    {
579       Bucket = I;
580       return I->String;
581    }
582    
583    // Get a structure
584    unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem));
585    if (Item == 0)
586       return 0;
587
588    // Fill in the structure
589    pkgCache::StringItem *ItemP = Cache.StringItemP + Item;
590    ItemP->NextItem = I - Cache.StringItemP;
591    *Last = Item;
592    ItemP->String = Map.WriteString(S,Size);
593    if (ItemP->String == 0)
594       return 0;
595    
596    Bucket = ItemP;
597    return ItemP->String;
598 }
599                                                                         /*}}}*/
600
601 // CheckValidity - Check that a cache is up-to-date                     /*{{{*/
602 // ---------------------------------------------------------------------
603 /* This just verifies that each file in the list of index files exists,
604    has matching attributes with the cache and the cache does not have
605    any extra files. */
606 static bool CheckValidity(string CacheFile, FileIterator Start, 
607                           FileIterator End,MMap **OutMap = 0)
608 {
609    // No file, certainly invalid
610    if (CacheFile.empty() == true || FileExists(CacheFile) == false)
611       return false;
612    
613    // CNC:2003-02-20 - When --reinstall is used during a cache building
614    //                  process, the algorithm is sligthly changed to
615    //                  order the "better" architectures before, even if
616    //                  they are already in the system. Thus, we rebuild
617    //                  the cache when it's used.
618    bool ReInstall = _config->FindB("APT::Get::ReInstall", false);
619    if (ReInstall == true)
620       return false;
621
622    // Map it
623    FileFd CacheF(CacheFile,FileFd::ReadOnly);
624    SPtr<MMap> Map = new MMap(CacheF,MMap::Public | MMap::ReadOnly);
625    pkgCache Cache(Map);
626    if (_error->PendingError() == true || Map->Size() == 0)
627    {
628       _error->Discard();
629       return false;
630    }
631    
632    // CNC:2003-11-24
633    if (_system->OptionsHash() != Cache.HeaderP->OptionsHash)
634       return false;
635
636    /* Now we check every index file, see if it is in the cache,
637       verify the IMS data and check that it is on the disk too.. */
638    SPtrArray<bool> Visited = new bool[Cache.HeaderP->PackageFileCount];
639    memset(Visited,0,sizeof(*Visited)*Cache.HeaderP->PackageFileCount);
640    for (; Start != End; Start++)
641    {      
642       if ((*Start)->HasPackages() == false)
643          continue;
644     
645       if ((*Start)->Exists() == false)
646       {
647          // CNC:2002-07-04
648          /*_error->WarningE("stat",_("Couldn't stat source package list %s"),
649                           (*Start)->Describe().c_str());*/
650          continue;
651       }
652
653       // FindInCache is also expected to do an IMS check.
654       pkgCache::PkgFileIterator File = (*Start)->FindInCache(Cache);
655       if (File.end() == true)
656          return false;
657       
658       Visited[File->ID] = true;
659    }
660    
661    for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++)
662       if (Visited[I] == false)
663          return false;
664    
665    if (_error->PendingError() == true)
666    {
667       _error->Discard();
668       return false;
669    }
670    
671    if (OutMap != 0)
672       *OutMap = Map.UnGuard();
673    return true;
674 }
675                                                                         /*}}}*/
676 // ComputeSize - Compute the total size of a bunch of files             /*{{{*/
677 // ---------------------------------------------------------------------
678 /* Size is kind of an abstract notion that is only used for the progress
679    meter */
680 static unsigned long ComputeSize(FileIterator Start,FileIterator End)
681 {
682    unsigned long TotalSize = 0;
683    for (; Start != End; Start++)
684    {
685       if ((*Start)->HasPackages() == false)
686          continue;      
687       TotalSize += (*Start)->Size();
688    }
689    return TotalSize;
690 }
691                                                                         /*}}}*/
692 // BuildCache - Merge the list of index files into the cache            /*{{{*/
693 // ---------------------------------------------------------------------
694 /* */
695 static bool BuildCache(pkgCacheGenerator &Gen,
696                        OpProgress &Progress,
697                        unsigned long &CurrentSize,unsigned long TotalSize,
698                        FileIterator Start, FileIterator End)
699 {
700    FileIterator I;
701    for (I = Start; I != End; I++)
702    {
703       if ((*I)->HasPackages() == false)
704          continue;
705       
706       if ((*I)->Exists() == false)
707          continue;
708
709       if ((*I)->FindInCache(Gen.GetCache()).end() == false)
710       {
711          _error->Warning("Duplicate sources.list entry %s",
712                          (*I)->Describe().c_str());
713          continue;
714       }
715       
716       unsigned long Size = (*I)->Size();
717       Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading Package Lists"));
718       CurrentSize += Size;
719       
720       if ((*I)->Merge(Gen,Progress) == false)
721          return false;
722    }   
723
724    // CNC:2003-03-03 - Code that was here has been moved to its own function.
725    
726    return true;
727 }
728                                                                         /*}}}*/
729 // CNC:2003-03-03
730 // CollectFileProvides - Merge the file provides into the cache         /*{{{*/
731 // ---------------------------------------------------------------------
732 /* */
733 static bool CollectFileProvides(pkgCacheGenerator &Gen,
734                                 OpProgress &Progress,
735                                 unsigned long &CurrentSize,unsigned long TotalSize,
736                                 FileIterator Start, FileIterator End)
737 {
738    for (FileIterator I = Start; I != End; I++)
739    {
740       if ((*I)->HasPackages() == false || (*I)->Exists() == false)
741          continue;
742
743       unsigned long Size = (*I)->Size();
744       Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading Package Lists"));
745       CurrentSize += Size;
746
747       if ((*I)->MergeFileProvides(Gen,Progress) == false)
748          return false;
749    }
750    return true;
751 }
752                                                                         /*}}}*/
753 // MakeStatusCache - Construct the status cache                         /*{{{*/
754 // ---------------------------------------------------------------------
755 /* This makes sure that the status cache (the cache that has all 
756    index files from the sources list and all local ones) is ready
757    to be mmaped. If OutMap is not zero then a MMap object representing
758    the cache will be stored there. This is pretty much mandetory if you
759    are using AllowMem. AllowMem lets the function be run as non-root
760    where it builds the cache 'fast' into a memory buffer. */
761 bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress,
762                         MMap **OutMap,bool AllowMem)
763 {
764    unsigned long MapSize = _config->FindI("APT::Cache-Limit",12*1024*1024);
765    
766    vector<pkgIndexFile *> Files(List.begin(),List.end());
767    unsigned long EndOfSource = Files.size();
768    if (_system->AddStatusFiles(Files) == false)
769       return false;
770    
771    // Decide if we can write to the files..
772    string CacheFile = _config->FindFile("Dir::Cache::pkgcache");
773    string SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
774    
775    // Decide if we can write to the cache
776    bool Writeable = false;
777    if (CacheFile.empty() == false)
778       Writeable = access(flNotFile(CacheFile).c_str(),W_OK) == 0;
779    else
780       if (SrcCacheFile.empty() == false)
781          Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0;
782    
783    if (Writeable == false && AllowMem == false && CacheFile.empty() == false)
784       return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str());
785    
786    Progress.OverallProgress(0,1,1,_("Reading Package Lists"));
787    
788    // Cache is OK, Fin.
789    if (CheckValidity(CacheFile,Files.begin(),Files.end(),OutMap) == true)
790    {
791       Progress.OverallProgress(1,1,1,_("Reading Package Lists"));
792       return true;
793    }
794    
795    // CNC:2002-07-03
796 #if DYING
797    if (_system->PreProcess(Files.begin(),Files.end(),Progress) == false) 
798    {
799        _error->Error(_("Error pre-processing package lists"));
800        return false;
801    }
802 #endif
803    /* At this point we know we need to reconstruct the package cache,
804       begin. */
805    SPtr<FileFd> CacheF;
806    SPtr<DynamicMMap> Map;
807    if (Writeable == true && CacheFile.empty() == false)
808    {
809       unlink(CacheFile.c_str());
810       CacheF = new FileFd(CacheFile,FileFd::WriteEmpty);
811       fchmod(CacheF->Fd(),0644);
812       Map = new DynamicMMap(*CacheF,MMap::Public,MapSize);
813       if (_error->PendingError() == true)
814          return false;
815    }
816    else
817    {
818       // Just build it in memory..
819       Map = new DynamicMMap(MMap::Public,MapSize);
820    }
821    
822    // Lets try the source cache.
823    unsigned long CurrentSize = 0;
824    unsigned long TotalSize = 0;
825    if (CheckValidity(SrcCacheFile,Files.begin(),
826                      Files.begin()+EndOfSource) == true)
827    {
828       // Preload the map with the source cache
829       FileFd SCacheF(SrcCacheFile,FileFd::ReadOnly);
830       if (SCacheF.Read((unsigned char *)Map->Data() + Map->RawAllocate(SCacheF.Size()),
831                        SCacheF.Size()) == false)
832          return false;
833
834       TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
835
836       // CNC:2003-03-18
837       // For the file provides collection phase.
838       unsigned long SrcSize = ComputeSize(Files.begin(),
839                                           Files.begin()+EndOfSource);
840       TotalSize = TotalSize+(TotalSize+SrcSize);
841       
842       // Build the status cache
843       pkgCacheGenerator Gen(Map.Get(),&Progress);
844       if (_error->PendingError() == true)
845          return false;
846       if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
847                      Files.begin()+EndOfSource,Files.end()) == false)
848          return false;
849
850       // CNC:2003-03-18
851       if (Gen.HasFileDeps() == true) {
852          // There are new file dependencies. Collect over all packages.
853          Gen.GetCache().HeaderP->HasFileDeps = true;
854          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
855                                  Files.begin(),Files.end()) == false)
856             return false;
857       } else if (Gen.GetCache().HeaderP->HasFileDeps == true) {
858          // Jump entries which are not going to be parsed.
859          CurrentSize += SrcSize;
860          // No new file dependencies. Collect over the new packages.
861          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
862                                  Files.begin()+EndOfSource,Files.end()) == false)
863             return false;
864       }
865    }
866    else
867    {
868       TotalSize = ComputeSize(Files.begin(),Files.end());
869
870       // CNC:2003-03-18
871       // For the file provides collection phase.
872       unsigned long SrcSize = ComputeSize(Files.begin(),
873                                           Files.begin()+EndOfSource);
874       TotalSize = (TotalSize*2)+SrcSize;
875       
876       // Build the source cache
877       pkgCacheGenerator Gen(Map.Get(),&Progress);
878       if (_error->PendingError() == true)
879          return false;
880       if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
881                      Files.begin(),Files.begin()+EndOfSource) == false)
882          return false;
883
884       // CNC:2003-11-24
885       Gen.GetCache().HeaderP->OptionsHash = _system->OptionsHash();
886
887       // CNC:2003-03-18
888       if (Gen.HasFileDeps() == true) {
889          // There are file dependencies. Collect over source packages.
890          Gen.GetCache().HeaderP->HasFileDeps = true;
891          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
892                      Files.begin(),Files.begin()+EndOfSource) == false)
893             return false;
894          // Reset to check for new file dependencies in the status cache.
895          Gen.ResetFileDeps();
896       } else {
897          // Jump entries which are not going to be parsed.
898          CurrentSize += SrcSize;
899       }
900       
901       // Write it back
902       // CNC:2003-03-03 - Notice that it is without the file provides. This
903       // is on purpose, since file requires introduced later on the status
904       // cache (database) must be considered when collecting file provides,
905       // even if using the sources cache (above).
906       if (Writeable == true && SrcCacheFile.empty() == false)
907       {
908          FileFd SCacheF(SrcCacheFile,FileFd::WriteEmpty);
909          if (_error->PendingError() == true)
910             return false;
911          
912          fchmod(SCacheF.Fd(),0644);
913          
914          // Write out the main data
915          if (SCacheF.Write(Map->Data(),Map->Size()) == false)
916             return _error->Error(_("IO Error saving source cache"));
917          SCacheF.Sync();
918          
919          // Write out the proper header
920          Gen.GetCache().HeaderP->Dirty = false;
921          if (SCacheF.Seek(0) == false ||
922              SCacheF.Write(Map->Data(),sizeof(*Gen.GetCache().HeaderP)) == false)
923             return _error->Error(_("IO Error saving source cache"));
924          Gen.GetCache().HeaderP->Dirty = true;
925          SCacheF.Sync();
926       }
927       
928       // Build the status cache
929       if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
930                      Files.begin()+EndOfSource,Files.end()) == false)
931          return false;
932
933       // CNC:2003-03-18
934       if (Gen.HasFileDeps() == true) {
935          // There are new file dependencies. Collect over all packages.
936          Gen.GetCache().HeaderP->HasFileDeps = true;
937          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
938                                  Files.begin(),Files.end()) == false)
939             return false;
940       } else if (Gen.GetCache().HeaderP->HasFileDeps == true) {
941          // Jump entries which are not going to be parsed.
942          CurrentSize += SrcSize;
943          // No new file dependencies. Collect over the new packages.
944          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
945                      Files.begin()+EndOfSource,Files.end()) == false)
946             return false;
947       }
948    }
949
950    if (_error->PendingError() == true)
951       return false;
952    if (OutMap != 0)
953    {
954       if (CacheF != 0)
955       {
956          delete Map.UnGuard();
957          *OutMap = new MMap(*CacheF,MMap::Public | MMap::ReadOnly);
958       }
959       else
960       {
961          *OutMap = Map.UnGuard();
962       }      
963    }
964
965    // CNC:2003-03-07 - Signal to the system so that it can free it's
966    //                  internal caches, if any.
967    _system->CacheBuilt();
968    
969    return true;
970 }
971                                                                         /*}}}*/
972 // MakeOnlyStatusCache - Build a cache with just the status files       /*{{{*/
973 // ---------------------------------------------------------------------
974 /* */
975 bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap)
976 {
977    unsigned long MapSize = _config->FindI("APT::Cache-Limit",8*1024*1024);
978    vector<pkgIndexFile *> Files;
979    unsigned long EndOfSource = Files.size();
980    if (_system->AddStatusFiles(Files) == false)
981       return false;
982    
983    SPtr<DynamicMMap> Map;   
984    Map = new DynamicMMap(MMap::Public,MapSize);
985    unsigned long CurrentSize = 0;
986    unsigned long TotalSize = 0;
987    
988    TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
989
990    // CNC:2003-03-18
991    // For the file provides collection phase.
992    TotalSize *= 2;
993    
994    // Build the status cache
995    Progress.OverallProgress(0,1,1,_("Reading Package Lists"));
996    pkgCacheGenerator Gen(Map.Get(),&Progress);
997    if (_error->PendingError() == true)
998       return false;
999    if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
1000                   Files.begin()+EndOfSource,Files.end()) == false)
1001       return false;
1002
1003    // CNC:2003-03-18
1004    if (Gen.HasFileDeps() == true) {
1005       if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
1006                               Files.begin()+EndOfSource,Files.end()) == false)
1007          return false;
1008    }
1009    
1010    if (_error->PendingError() == true)
1011       return false;
1012    *OutMap = Map.UnGuard();
1013
1014    // CNC:2003-03-07 - Signal to the system so that it can free it's
1015    //                  internal caches, if any.
1016    _system->CacheBuilt();
1017    
1018    
1019    return true;
1020 }
1021                                                                         /*}}}*/
1022
1023 // vim:sts=3:sw=3