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