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