- initial import of revision 374 from cnc
[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    // We do not add self referencing provides
485    if (Ver.ParentPkg().Name() == PackageName)
486       return true;
487    
488    // Get a structure
489    unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides));
490    if (Provides == 0)
491       return false;
492    Cache.HeaderP->ProvidesCount++;
493    
494    // Fill it in
495    pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
496    Prv->Version = Ver.Index();
497    Prv->NextPkgProv = Ver->ProvidesList;
498    Ver->ProvidesList = Prv.Index();
499    if (Version.empty() == false && (Prv->ProvideVersion = WriteString(Version)) == 0)
500       return false;
501    
502    // Locate the target package
503    pkgCache::PkgIterator Pkg;
504    if (Owner->NewPackage(Pkg,PackageName) == false)
505       return false;
506    
507    // Link it to the package
508    Prv->ParentPkg = Pkg.Index();
509    Prv->NextProvides = Pkg->ProvidesList;
510    Pkg->ProvidesList = Prv.Index();
511    
512    return true;
513 }
514                                                                         /*}}}*/
515 // CacheGenerator::SelectFile - Select the current file being parsed    /*{{{*/
516 // ---------------------------------------------------------------------
517 /* This is used to select which file is to be associated with all newly
518    added versions. The caller is responsible for setting the IMS fields. */
519 bool pkgCacheGenerator::SelectFile(string File,string Site,
520                                    const pkgIndexFile &Index,
521                                    unsigned long Flags)
522 {
523    // Get some space for the structure
524    CurrentFile = Cache.PkgFileP + Map.Allocate(sizeof(*CurrentFile));
525    if (CurrentFile == Cache.PkgFileP)
526       return false;
527    
528    // Fill it in
529    CurrentFile->FileName = Map.WriteString(File);
530    CurrentFile->Site = WriteUniqString(Site);
531    CurrentFile->NextFile = Cache.HeaderP->FileList;
532    CurrentFile->Flags = Flags;
533    CurrentFile->ID = Cache.HeaderP->PackageFileCount;
534    CurrentFile->IndexType = WriteUniqString(Index.GetType()->Label);
535    PkgFileName = File;
536    Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
537    Cache.HeaderP->PackageFileCount++;
538
539    if (CurrentFile->FileName == 0)
540       return false;
541    
542    if (Progress != 0)
543       Progress->SubProgress(Index.Size());
544    return true;
545 }
546                                                                         /*}}}*/
547 // CacheGenerator::WriteUniqueString - Insert a unique string           /*{{{*/
548 // ---------------------------------------------------------------------
549 /* This is used to create handles to strings. Given the same text it
550    always returns the same number */
551 unsigned long pkgCacheGenerator::WriteUniqString(const char *S,
552                                                  unsigned int Size)
553 {
554    /* We use a very small transient hash table here, this speeds up generation
555       by a fair amount on slower machines */
556    pkgCache::StringItem *&Bucket = UniqHash[(S[0]*5 + S[1]) % _count(UniqHash)];
557    if (Bucket != 0 && 
558        stringcmp(S,S+Size,Cache.StrP + Bucket->String) == 0)
559       return Bucket->String;
560    
561    // Search for an insertion point
562    pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList;
563    int Res = 1;
564    map_ptrloc *Last = &Cache.HeaderP->StringList;
565    for (; I != Cache.StringItemP; Last = &I->NextItem, 
566         I = Cache.StringItemP + I->NextItem)
567    {
568       Res = stringcmp(S,S+Size,Cache.StrP + I->String);
569       if (Res >= 0)
570          break;
571    }
572    
573    // Match
574    if (Res == 0)
575    {
576       Bucket = I;
577       return I->String;
578    }
579    
580    // Get a structure
581    unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem));
582    if (Item == 0)
583       return 0;
584
585    // Fill in the structure
586    pkgCache::StringItem *ItemP = Cache.StringItemP + Item;
587    ItemP->NextItem = I - Cache.StringItemP;
588    *Last = Item;
589    ItemP->String = Map.WriteString(S,Size);
590    if (ItemP->String == 0)
591       return 0;
592    
593    Bucket = ItemP;
594    return ItemP->String;
595 }
596                                                                         /*}}}*/
597
598 // CheckValidity - Check that a cache is up-to-date                     /*{{{*/
599 // ---------------------------------------------------------------------
600 /* This just verifies that each file in the list of index files exists,
601    has matching attributes with the cache and the cache does not have
602    any extra files. */
603 static bool CheckValidity(string CacheFile, FileIterator Start, 
604                           FileIterator End,MMap **OutMap = 0)
605 {
606    // No file, certainly invalid
607    if (CacheFile.empty() == true || FileExists(CacheFile) == false)
608       return false;
609    
610    // CNC:2003-02-20 - When --reinstall is used during a cache building
611    //                  process, the algorithm is sligthly changed to
612    //                  order the "better" architectures before, even if
613    //                  they are already in the system. Thus, we rebuild
614    //                  the cache when it's used.
615    bool ReInstall = _config->FindB("APT::Get::ReInstall", false);
616    if (ReInstall == true)
617       return false;
618
619    // Map it
620    FileFd CacheF(CacheFile,FileFd::ReadOnly);
621    SPtr<MMap> Map = new MMap(CacheF,MMap::Public | MMap::ReadOnly);
622    pkgCache Cache(Map);
623    if (_error->PendingError() == true || Map->Size() == 0)
624    {
625       _error->Discard();
626       return false;
627    }
628    
629    // CNC:2003-11-24
630    if (_system->OptionsHash() != Cache.HeaderP->OptionsHash)
631       return false;
632
633    /* Now we check every index file, see if it is in the cache,
634       verify the IMS data and check that it is on the disk too.. */
635    SPtrArray<bool> Visited = new bool[Cache.HeaderP->PackageFileCount];
636    memset(Visited,0,sizeof(*Visited)*Cache.HeaderP->PackageFileCount);
637    for (; Start != End; Start++)
638    {      
639       if ((*Start)->HasPackages() == false)
640          continue;
641     
642       if ((*Start)->Exists() == false)
643       {
644          // CNC:2002-07-04
645          /*_error->WarningE("stat",_("Couldn't stat source package list %s"),
646                           (*Start)->Describe().c_str());*/
647          continue;
648       }
649
650       // FindInCache is also expected to do an IMS check.
651       pkgCache::PkgFileIterator File = (*Start)->FindInCache(Cache);
652       if (File.end() == true)
653          return false;
654       
655       Visited[File->ID] = true;
656    }
657    
658    for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++)
659       if (Visited[I] == false)
660          return false;
661    
662    if (_error->PendingError() == true)
663    {
664       _error->Discard();
665       return false;
666    }
667    
668    if (OutMap != 0)
669       *OutMap = Map.UnGuard();
670    return true;
671 }
672                                                                         /*}}}*/
673 // ComputeSize - Compute the total size of a bunch of files             /*{{{*/
674 // ---------------------------------------------------------------------
675 /* Size is kind of an abstract notion that is only used for the progress
676    meter */
677 static unsigned long ComputeSize(FileIterator Start,FileIterator End)
678 {
679    unsigned long TotalSize = 0;
680    for (; Start != End; Start++)
681    {
682       if ((*Start)->HasPackages() == false)
683          continue;      
684       TotalSize += (*Start)->Size();
685    }
686    return TotalSize;
687 }
688                                                                         /*}}}*/
689 // BuildCache - Merge the list of index files into the cache            /*{{{*/
690 // ---------------------------------------------------------------------
691 /* */
692 static bool BuildCache(pkgCacheGenerator &Gen,
693                        OpProgress &Progress,
694                        unsigned long &CurrentSize,unsigned long TotalSize,
695                        FileIterator Start, FileIterator End)
696 {
697    FileIterator I;
698    for (I = Start; I != End; I++)
699    {
700       if ((*I)->HasPackages() == false)
701          continue;
702       
703       if ((*I)->Exists() == false)
704          continue;
705
706       if ((*I)->FindInCache(Gen.GetCache()).end() == false)
707       {
708          _error->Warning("Duplicate sources.list entry %s",
709                          (*I)->Describe().c_str());
710          continue;
711       }
712       
713       unsigned long Size = (*I)->Size();
714       Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading Package Lists"));
715       CurrentSize += Size;
716       
717       if ((*I)->Merge(Gen,Progress) == false)
718          return false;
719    }   
720
721    // CNC:2003-03-03 - Code that was here has been moved to its own function.
722    
723    return true;
724 }
725                                                                         /*}}}*/
726 // CNC:2003-03-03
727 // CollectFileProvides - Merge the file provides into the cache         /*{{{*/
728 // ---------------------------------------------------------------------
729 /* */
730 static bool CollectFileProvides(pkgCacheGenerator &Gen,
731                                 OpProgress &Progress,
732                                 unsigned long &CurrentSize,unsigned long TotalSize,
733                                 FileIterator Start, FileIterator End)
734 {
735    for (FileIterator I = Start; I != End; I++)
736    {
737       if ((*I)->HasPackages() == false || (*I)->Exists() == false)
738          continue;
739
740       unsigned long Size = (*I)->Size();
741       Progress.OverallProgress(CurrentSize,TotalSize,Size,_("Reading Package Lists"));
742       CurrentSize += Size;
743
744       if ((*I)->MergeFileProvides(Gen,Progress) == false)
745          return false;
746    }
747    return true;
748 }
749                                                                         /*}}}*/
750 // MakeStatusCache - Construct the status cache                         /*{{{*/
751 // ---------------------------------------------------------------------
752 /* This makes sure that the status cache (the cache that has all 
753    index files from the sources list and all local ones) is ready
754    to be mmaped. If OutMap is not zero then a MMap object representing
755    the cache will be stored there. This is pretty much mandetory if you
756    are using AllowMem. AllowMem lets the function be run as non-root
757    where it builds the cache 'fast' into a memory buffer. */
758 bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress,
759                         MMap **OutMap,bool AllowMem)
760 {
761    unsigned long MapSize = _config->FindI("APT::Cache-Limit",12*1024*1024);
762    
763    vector<pkgIndexFile *> Files(List.begin(),List.end());
764    unsigned long EndOfSource = Files.size();
765    if (_system->AddStatusFiles(Files) == false)
766       return false;
767    
768    // Decide if we can write to the files..
769    string CacheFile = _config->FindFile("Dir::Cache::pkgcache");
770    string SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
771    
772    // Decide if we can write to the cache
773    bool Writeable = false;
774    if (CacheFile.empty() == false)
775       Writeable = access(flNotFile(CacheFile).c_str(),W_OK) == 0;
776    else
777       if (SrcCacheFile.empty() == false)
778          Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0;
779    
780    if (Writeable == false && AllowMem == false && CacheFile.empty() == false)
781       return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str());
782    
783    Progress.OverallProgress(0,1,1,_("Reading Package Lists"));
784    
785    // Cache is OK, Fin.
786    if (CheckValidity(CacheFile,Files.begin(),Files.end(),OutMap) == true)
787    {
788       Progress.OverallProgress(1,1,1,_("Reading Package Lists"));
789       return true;
790    }
791    
792    // CNC:2002-07-03
793 #if DYING
794    if (_system->PreProcess(Files.begin(),Files.end(),Progress) == false) 
795    {
796        _error->Error(_("Error pre-processing package lists"));
797        return false;
798    }
799 #endif
800    /* At this point we know we need to reconstruct the package cache,
801       begin. */
802    SPtr<FileFd> CacheF;
803    SPtr<DynamicMMap> Map;
804    if (Writeable == true && CacheFile.empty() == false)
805    {
806       unlink(CacheFile.c_str());
807       CacheF = new FileFd(CacheFile,FileFd::WriteEmpty);
808       fchmod(CacheF->Fd(),0644);
809       Map = new DynamicMMap(*CacheF,MMap::Public,MapSize);
810       if (_error->PendingError() == true)
811          return false;
812    }
813    else
814    {
815       // Just build it in memory..
816       Map = new DynamicMMap(MMap::Public,MapSize);
817    }
818    
819    // Lets try the source cache.
820    unsigned long CurrentSize = 0;
821    unsigned long TotalSize = 0;
822    if (CheckValidity(SrcCacheFile,Files.begin(),
823                      Files.begin()+EndOfSource) == true)
824    {
825       // Preload the map with the source cache
826       FileFd SCacheF(SrcCacheFile,FileFd::ReadOnly);
827       if (SCacheF.Read((unsigned char *)Map->Data() + Map->RawAllocate(SCacheF.Size()),
828                        SCacheF.Size()) == false)
829          return false;
830
831       TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
832
833       // CNC:2003-03-18
834       // For the file provides collection phase.
835       unsigned long SrcSize = ComputeSize(Files.begin(),
836                                           Files.begin()+EndOfSource);
837       TotalSize = TotalSize+(TotalSize+SrcSize);
838       
839       // Build the status cache
840       pkgCacheGenerator Gen(Map.Get(),&Progress);
841       if (_error->PendingError() == true)
842          return false;
843       if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
844                      Files.begin()+EndOfSource,Files.end()) == false)
845          return false;
846
847       // CNC:2003-03-18
848       if (Gen.HasFileDeps() == true) {
849          // There are new file dependencies. Collect over all packages.
850          Gen.GetCache().HeaderP->HasFileDeps = true;
851          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
852                                  Files.begin(),Files.end()) == false)
853             return false;
854       } else if (Gen.GetCache().HeaderP->HasFileDeps == true) {
855          // Jump entries which are not going to be parsed.
856          CurrentSize += SrcSize;
857          // No new file dependencies. Collect over the new packages.
858          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
859                                  Files.begin()+EndOfSource,Files.end()) == false)
860             return false;
861       }
862    }
863    else
864    {
865       TotalSize = ComputeSize(Files.begin(),Files.end());
866
867       // CNC:2003-03-18
868       // For the file provides collection phase.
869       unsigned long SrcSize = ComputeSize(Files.begin(),
870                                           Files.begin()+EndOfSource);
871       TotalSize = (TotalSize*2)+SrcSize;
872       
873       // Build the source cache
874       pkgCacheGenerator Gen(Map.Get(),&Progress);
875       if (_error->PendingError() == true)
876          return false;
877       if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
878                      Files.begin(),Files.begin()+EndOfSource) == false)
879          return false;
880
881       // CNC:2003-11-24
882       Gen.GetCache().HeaderP->OptionsHash = _system->OptionsHash();
883
884       // CNC:2003-03-18
885       if (Gen.HasFileDeps() == true) {
886          // There are file dependencies. Collect over source packages.
887          Gen.GetCache().HeaderP->HasFileDeps = true;
888          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
889                      Files.begin(),Files.begin()+EndOfSource) == false)
890             return false;
891          // Reset to check for new file dependencies in the status cache.
892          Gen.ResetFileDeps();
893       } else {
894          // Jump entries which are not going to be parsed.
895          CurrentSize += SrcSize;
896       }
897       
898       // Write it back
899       // CNC:2003-03-03 - Notice that it is without the file provides. This
900       // is on purpose, since file requires introduced later on the status
901       // cache (database) must be considered when collecting file provides,
902       // even if using the sources cache (above).
903       if (Writeable == true && SrcCacheFile.empty() == false)
904       {
905          FileFd SCacheF(SrcCacheFile,FileFd::WriteEmpty);
906          if (_error->PendingError() == true)
907             return false;
908          
909          fchmod(SCacheF.Fd(),0644);
910          
911          // Write out the main data
912          if (SCacheF.Write(Map->Data(),Map->Size()) == false)
913             return _error->Error(_("IO Error saving source cache"));
914          SCacheF.Sync();
915          
916          // Write out the proper header
917          Gen.GetCache().HeaderP->Dirty = false;
918          if (SCacheF.Seek(0) == false ||
919              SCacheF.Write(Map->Data(),sizeof(*Gen.GetCache().HeaderP)) == false)
920             return _error->Error(_("IO Error saving source cache"));
921          Gen.GetCache().HeaderP->Dirty = true;
922          SCacheF.Sync();
923       }
924       
925       // Build the status cache
926       if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
927                      Files.begin()+EndOfSource,Files.end()) == false)
928          return false;
929
930       // CNC:2003-03-18
931       if (Gen.HasFileDeps() == true) {
932          // There are new file dependencies. Collect over all packages.
933          Gen.GetCache().HeaderP->HasFileDeps = true;
934          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
935                                  Files.begin(),Files.end()) == false)
936             return false;
937       } else if (Gen.GetCache().HeaderP->HasFileDeps == true) {
938          // Jump entries which are not going to be parsed.
939          CurrentSize += SrcSize;
940          // No new file dependencies. Collect over the new packages.
941          if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
942                      Files.begin()+EndOfSource,Files.end()) == false)
943             return false;
944       }
945    }
946
947    if (_error->PendingError() == true)
948       return false;
949    if (OutMap != 0)
950    {
951       if (CacheF != 0)
952       {
953          delete Map.UnGuard();
954          *OutMap = new MMap(*CacheF,MMap::Public | MMap::ReadOnly);
955       }
956       else
957       {
958          *OutMap = Map.UnGuard();
959       }      
960    }
961
962    // CNC:2003-03-07 - Signal to the system so that it can free it's
963    //                  internal caches, if any.
964    _system->CacheBuilt();
965    
966    return true;
967 }
968                                                                         /*}}}*/
969 // MakeOnlyStatusCache - Build a cache with just the status files       /*{{{*/
970 // ---------------------------------------------------------------------
971 /* */
972 bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap)
973 {
974    unsigned long MapSize = _config->FindI("APT::Cache-Limit",8*1024*1024);
975    vector<pkgIndexFile *> Files;
976    unsigned long EndOfSource = Files.size();
977    if (_system->AddStatusFiles(Files) == false)
978       return false;
979    
980    SPtr<DynamicMMap> Map;   
981    Map = new DynamicMMap(MMap::Public,MapSize);
982    unsigned long CurrentSize = 0;
983    unsigned long TotalSize = 0;
984    
985    TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
986
987    // CNC:2003-03-18
988    // For the file provides collection phase.
989    TotalSize *= 2;
990    
991    // Build the status cache
992    Progress.OverallProgress(0,1,1,_("Reading Package Lists"));
993    pkgCacheGenerator Gen(Map.Get(),&Progress);
994    if (_error->PendingError() == true)
995       return false;
996    if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
997                   Files.begin()+EndOfSource,Files.end()) == false)
998       return false;
999
1000    // CNC:2003-03-18
1001    if (Gen.HasFileDeps() == true) {
1002       if (CollectFileProvides(Gen,Progress,CurrentSize,TotalSize,
1003                               Files.begin()+EndOfSource,Files.end()) == false)
1004          return false;
1005    }
1006    
1007    if (_error->PendingError() == true)
1008       return false;
1009    *OutMap = Map.UnGuard();
1010
1011    // CNC:2003-03-07 - Signal to the system so that it can free it's
1012    //                  internal caches, if any.
1013    _system->CacheBuilt();
1014    
1015    
1016    return true;
1017 }
1018                                                                         /*}}}*/
1019
1020 // vim:sts=3:sw=3