]> git.saurik.com Git - apt.git/blame - apt-pkg/pkgcachegen.cc
* apt-pkg/indexfile.cc:
[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 {
770c32ec
MV
128 // we first process the package, then the descriptions
129 // (this has the bonus that we get MMap error when we run out
130 // of MMap space)
131 if (List.UsePackage(Pkg,pkgCache::VerIterator(Cache)) == false)
132 return _error->Error(_("Error occurred while processing %s (UsePackage1)"),
133 PackageName.c_str());
134
a52f938b
OS
135 // Find the right version to write the description
136 MD5SumValue CurMd5 = List.Description_md5();
137 pkgCache::VerIterator Ver = Pkg.VersionList();
138 map_ptrloc *LastVer = &Pkg->VersionList;
770c32ec 139
a52f938b
OS
140 for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++)
141 {
142 pkgCache::DescIterator Desc = Ver.DescriptionList();
143 map_ptrloc *LastDesc = &Ver->DescriptionList;
770c32ec 144
a52f938b 145 for (; Desc.end() == false; LastDesc = &Desc->NextDesc, Desc++)
770c32ec
MV
146 {
147
148 if (MD5SumValue(Desc.md5()) == CurMd5)
149 {
a52f938b
OS
150 // Add new description
151 *LastDesc = NewDescription(Desc, List.DescriptionLanguage(), CurMd5, *LastDesc);
152 Desc->ParentPkg = Pkg.Index();
770c32ec 153
a52f938b
OS
154 if (NewFileDesc(Desc,List) == false)
155 return _error->Error(_("Error occured while processing %s (NewFileDesc1)"),PackageName.c_str());
156 break;
157 }
770c32ec 158 }
a52f938b 159 }
770c32ec 160
f55a958f
AL
161 continue;
162 }
163
578bfd0a 164 pkgCache::VerIterator Ver = Pkg.VersionList();
a52f938b 165 map_ptrloc *LastVer = &Pkg->VersionList;
2246928b 166 int Res = 1;
a52f938b 167 for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++)
578bfd0a 168 {
c24972cb 169 Res = Cache.VS->CmpVersion(Version,Ver.VerStr());
578bfd0a
AL
170 if (Res >= 0)
171 break;
172 }
173
174 /* We already have a version for this item, record that we
175 saw it */
204fbdcc
AL
176 unsigned long Hash = List.VersionHash();
177 if (Res == 0 && Ver->Hash == Hash)
578bfd0a 178 {
f55a958f 179 if (List.UsePackage(Pkg,Ver) == false)
0432d731 180 return _error->Error(_("Error occurred while processing %s (UsePackage2)"),
7a3c2ab0 181 PackageName.c_str());
f78439bf 182
578bfd0a 183 if (NewFileVer(Ver,List) == false)
0432d731 184 return _error->Error(_("Error occurred while processing %s (NewFileVer1)"),
7a3c2ab0 185 PackageName.c_str());
578bfd0a 186
ddc1d8d0
AL
187 // Read only a single record and return
188 if (OutVer != 0)
189 {
190 *OutVer = Ver;
45415543 191 FoundFileDeps |= List.HasFileDeps();
ddc1d8d0
AL
192 return true;
193 }
194
578bfd0a
AL
195 continue;
196 }
197
204fbdcc
AL
198 // Skip to the end of the same version set.
199 if (Res == 0)
200 {
a52f938b 201 for (; Ver.end() == false; LastVer = &Ver->NextVer, Ver++)
204fbdcc 202 {
c24972cb 203 Res = Cache.VS->CmpVersion(Version,Ver.VerStr());
204fbdcc
AL
204 if (Res != 0)
205 break;
206 }
207 }
208
578bfd0a 209 // Add a new version
a52f938b 210 *LastVer = NewVersion(Ver,Version,*LastVer);
f55a958f 211 Ver->ParentPkg = Pkg.Index();
204fbdcc 212 Ver->Hash = Hash;
a52f938b 213
578bfd0a 214 if (List.NewVersion(Ver) == false)
0432d731 215 return _error->Error(_("Error occurred while processing %s (NewVersion1)"),
7a3c2ab0 216 PackageName.c_str());
0149949b 217
f55a958f 218 if (List.UsePackage(Pkg,Ver) == false)
0432d731 219 return _error->Error(_("Error occurred while processing %s (UsePackage3)"),
7a3c2ab0 220 PackageName.c_str());
f55a958f 221
578bfd0a 222 if (NewFileVer(Ver,List) == false)
0432d731 223 return _error->Error(_("Error occurred while processing %s (NewVersion2)"),
7a3c2ab0 224 PackageName.c_str());
ddc1d8d0
AL
225
226 // Read only a single record and return
227 if (OutVer != 0)
228 {
229 *OutVer = Ver;
45415543 230 FoundFileDeps |= List.HasFileDeps();
ddc1d8d0
AL
231 return true;
232 }
a52f938b
OS
233
234 /* Record the Description data. Description data always exist in
235 Packages and Translation-* files. */
236 pkgCache::DescIterator Desc = Ver.DescriptionList();
237 map_ptrloc *LastDesc = &Ver->DescriptionList;
238
239 // Skip to the end of description set
240 for (; Desc.end() == false; LastDesc = &Desc->NextDesc, Desc++);
241
242 // Add new description
243 *LastDesc = NewDescription(Desc, List.DescriptionLanguage(), List.Description_md5(), *LastDesc);
244 Desc->ParentPkg = Pkg.Index();
245
246 if (NewFileDesc(Desc,List) == false)
247 return _error->Error(_("Error occured while processing %s (NewFileDesc2)"),PackageName.c_str());
578bfd0a 248 }
0149949b 249
45415543
AL
250 FoundFileDeps |= List.HasFileDeps();
251
6a3da7a6
AL
252 if (Cache.HeaderP->PackageCount >= (1ULL<<sizeof(Cache.PkgP->ID)*8)-1)
253 return _error->Error(_("Wow, you exceeded the number of package "
254 "names this APT is capable of."));
255 if (Cache.HeaderP->VersionCount >= (1ULL<<(sizeof(Cache.VerP->ID)*8))-1)
256 return _error->Error(_("Wow, you exceeded the number of versions "
257 "this APT is capable of."));
a52f938b
OS
258 if (Cache.HeaderP->DescriptionCount >= (1ULL<<(sizeof(Cache.DescP->ID)*8))-1)
259 return _error->Error(_("Wow, you exceeded the number of descriptions "
260 "this APT is capable of."));
6a3da7a6
AL
261 if (Cache.HeaderP->DependsCount >= (1ULL<<(sizeof(Cache.DepP->ID)*8))-1ULL)
262 return _error->Error(_("Wow, you exceeded the number of dependencies "
263 "this APT is capable of."));
578bfd0a
AL
264 return true;
265}
266 /*}}}*/
45415543
AL
267// CacheGenerator::MergeFileProvides - Merge file provides /*{{{*/
268// ---------------------------------------------------------------------
269/* If we found any file depends while parsing the main list we need to
270 resolve them. Since it is undesired to load the entire list of files
271 into the cache as virtual packages we do a two stage effort. MergeList
272 identifies the file depends and this creates Provdies for them by
273 re-parsing all the indexs. */
274bool pkgCacheGenerator::MergeFileProvides(ListParser &List)
275{
276 List.Owner = this;
277
278 unsigned int Counter = 0;
279 while (List.Step() == true)
280 {
281 string PackageName = List.Package();
282 if (PackageName.empty() == true)
283 return false;
284 string Version = List.Version();
285 if (Version.empty() == true)
286 continue;
287
288 pkgCache::PkgIterator Pkg = Cache.FindPkg(PackageName);
289 if (Pkg.end() == true)
0432d731 290 return _error->Error(_("Error occurred while processing %s (FindPkg)"),
45415543
AL
291 PackageName.c_str());
292 Counter++;
293 if (Counter % 100 == 0 && Progress != 0)
294 Progress->Progress(List.Offset());
295
296 unsigned long Hash = List.VersionHash();
297 pkgCache::VerIterator Ver = Pkg.VersionList();
298 for (; Ver.end() == false; Ver++)
299 {
300 if (Ver->Hash == Hash && Version.c_str() == Ver.VerStr())
301 {
302 if (List.CollectFileProvides(Cache,Ver) == false)
0432d731 303 return _error->Error(_("Error occurred while processing %s (CollectFileProvides)"),PackageName.c_str());
45415543
AL
304 break;
305 }
306 }
307
308 if (Ver.end() == true)
309 _error->Warning(_("Package %s %s was not found while processing file dependencies"),PackageName.c_str(),Version.c_str());
310 }
311
312 return true;
313}
314 /*}}}*/
578bfd0a
AL
315// CacheGenerator::NewPackage - Add a new package /*{{{*/
316// ---------------------------------------------------------------------
317/* This creates a new package structure and adds it to the hash table */
f55a958f 318bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,string Name)
578bfd0a 319{
8efa2a3b
AL
320 Pkg = Cache.FindPkg(Name);
321 if (Pkg.end() == false)
322 return true;
a52f938b 323
578bfd0a
AL
324 // Get a structure
325 unsigned long Package = Map.Allocate(sizeof(pkgCache::Package));
326 if (Package == 0)
327 return false;
328
f55a958f 329 Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package);
578bfd0a
AL
330
331 // Insert it into the hash table
f55a958f 332 unsigned long Hash = Cache.Hash(Name);
578bfd0a
AL
333 Pkg->NextPackage = Cache.HeaderP->HashTable[Hash];
334 Cache.HeaderP->HashTable[Hash] = Package;
335
336 // Set the name and the ID
337 Pkg->Name = Map.WriteString(Name);
338 if (Pkg->Name == 0)
339 return false;
340 Pkg->ID = Cache.HeaderP->PackageCount++;
341
342 return true;
343}
344 /*}}}*/
345// CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
346// ---------------------------------------------------------------------
347/* */
f55a958f 348bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver,
578bfd0a
AL
349 ListParser &List)
350{
ddc1d8d0
AL
351 if (CurrentFile == 0)
352 return true;
353
dcb79bae
AL
354 // Get a structure
355 unsigned long VerFile = Map.Allocate(sizeof(pkgCache::VerFile));
356 if (VerFile == 0)
357 return 0;
358
359 pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile);
360 VF->File = CurrentFile - Cache.PkgFileP;
03e39e59
AL
361
362 // Link it to the end of the list
349cd3b8 363 map_ptrloc *Last = &Ver->FileList;
03e39e59
AL
364 for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; V++)
365 Last = &V->NextFile;
366 VF->NextFile = *Last;
367 *Last = VF.Index();
368
dcb79bae
AL
369 VF->Offset = List.Offset();
370 VF->Size = List.Size();
ad00ae81
AL
371 if (Cache.HeaderP->MaxVerFileSize < VF->Size)
372 Cache.HeaderP->MaxVerFileSize = VF->Size;
a7e66b17
AL
373 Cache.HeaderP->VerFileCount++;
374
f55a958f 375 return true;
578bfd0a
AL
376}
377 /*}}}*/
378// CacheGenerator::NewVersion - Create a new Version /*{{{*/
379// ---------------------------------------------------------------------
f55a958f 380/* This puts a version structure in the linked list */
578bfd0a 381unsigned long pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver,
f55a958f 382 string VerStr,
578bfd0a
AL
383 unsigned long Next)
384{
f55a958f
AL
385 // Get a structure
386 unsigned long Version = Map.Allocate(sizeof(pkgCache::Version));
387 if (Version == 0)
0149949b 388 return 0;
f55a958f
AL
389
390 // Fill it in
391 Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
f55a958f
AL
392 Ver->NextVer = Next;
393 Ver->ID = Cache.HeaderP->VersionCount++;
394 Ver->VerStr = Map.WriteString(VerStr);
395 if (Ver->VerStr == 0)
0149949b 396 return 0;
f55a958f 397
0149949b 398 return Version;
578bfd0a
AL
399}
400 /*}}}*/
a52f938b
OS
401// CacheGenerator::NewFileDesc - Create a new File<->Desc association /*{{{*/
402// ---------------------------------------------------------------------
403/* */
404bool pkgCacheGenerator::NewFileDesc(pkgCache::DescIterator &Desc,
405 ListParser &List)
406{
407 if (CurrentFile == 0)
408 return true;
409
410 // Get a structure
411 unsigned long DescFile = Map.Allocate(sizeof(pkgCache::DescFile));
412 if (DescFile == 0)
413 return 0;
770c32ec 414
a52f938b
OS
415 pkgCache::DescFileIterator DF(Cache,Cache.DescFileP + DescFile);
416 DF->File = CurrentFile - Cache.PkgFileP;
770c32ec 417
a52f938b
OS
418 // Link it to the end of the list
419 map_ptrloc *Last = &Desc->FileList;
420 for (pkgCache::DescFileIterator D = Desc.FileList(); D.end() == false; D++)
421 Last = &D->NextFile;
770c32ec 422
a52f938b
OS
423 DF->NextFile = *Last;
424 *Last = DF.Index();
425
426 DF->Offset = List.Offset();
427 DF->Size = List.Size();
428 if (Cache.HeaderP->MaxDescFileSize < DF->Size)
429 Cache.HeaderP->MaxDescFileSize = DF->Size;
430 Cache.HeaderP->DescFileCount++;
431
432 return true;
433}
434 /*}}}*/
435// CacheGenerator::NewDescription - Create a new Description /*{{{*/
436// ---------------------------------------------------------------------
437/* This puts a description structure in the linked list */
438map_ptrloc pkgCacheGenerator::NewDescription(pkgCache::DescIterator &Desc,
439 const string &Lang, const MD5SumValue &md5sum,
440 map_ptrloc Next)
441{
442 // Get a structure
443 map_ptrloc Description = Map.Allocate(sizeof(pkgCache::Description));
444 if (Description == 0)
445 return 0;
446
447 // Fill it in
448 Desc = pkgCache::DescIterator(Cache,Cache.DescP + Description);
449 Desc->NextDesc = Next;
450 Desc->ID = Cache.HeaderP->DescriptionCount++;
451 Desc->language_code = Map.WriteString(Lang);
452 Desc->md5sum = Map.WriteString(md5sum.Value());
453
454 return Description;
455}
456 /*}}}*/
dcb79bae
AL
457// ListParser::NewDepends - Create a dependency element /*{{{*/
458// ---------------------------------------------------------------------
459/* This creates a dependency element in the tree. It is linked to the
460 version and to the package that it is pointing to. */
461bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver,
9ddf7030
AL
462 string PackageName,
463 string Version,
dcb79bae
AL
464 unsigned int Op,
465 unsigned int Type)
466{
467 pkgCache &Cache = Owner->Cache;
468
469 // Get a structure
470 unsigned long Dependency = Owner->Map.Allocate(sizeof(pkgCache::Dependency));
471 if (Dependency == 0)
472 return false;
473
474 // Fill it in
475 pkgCache::DepIterator Dep(Cache,Cache.DepP + Dependency);
476 Dep->ParentVer = Ver.Index();
477 Dep->Type = Type;
478 Dep->CompareOp = Op;
479 Dep->ID = Cache.HeaderP->DependsCount++;
480
481 // Locate the target package
8efa2a3b
AL
482 pkgCache::PkgIterator Pkg;
483 if (Owner->NewPackage(Pkg,PackageName) == false)
484 return false;
dcb79bae
AL
485
486 // Probe the reverse dependency list for a version string that matches
487 if (Version.empty() == false)
488 {
b2e465d6 489/* for (pkgCache::DepIterator I = Pkg.RevDependsList(); I.end() == false; I++)
dcb79bae 490 if (I->Version != 0 && I.TargetVer() == Version)
f9eec0e7 491 Dep->Version = I->Version;*/
dcb79bae
AL
492 if (Dep->Version == 0)
493 if ((Dep->Version = WriteString(Version)) == 0)
494 return false;
495 }
c1a22377 496
dcb79bae
AL
497 // Link it to the package
498 Dep->Package = Pkg.Index();
499 Dep->NextRevDepends = Pkg->RevDepends;
500 Pkg->RevDepends = Dep.Index();
501
c1a22377
AL
502 /* Link it to the version (at the end of the list)
503 Caching the old end point speeds up generation substantially */
f9eec0e7 504 if (OldDepVer != Ver)
c1a22377 505 {
f9eec0e7 506 OldDepLast = &Ver->DependsList;
c1a22377 507 for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
f9eec0e7
AL
508 OldDepLast = &D->NextDepends;
509 OldDepVer = Ver;
c1a22377 510 }
45415543
AL
511
512 // Is it a file dependency?
513 if (PackageName[0] == '/')
514 FoundFileDeps = true;
dcb79bae 515
f9eec0e7
AL
516 Dep->NextDepends = *OldDepLast;
517 *OldDepLast = Dep.Index();
518 OldDepLast = &Dep->NextDepends;
c1a22377 519
dcb79bae
AL
520 return true;
521}
522 /*}}}*/
523// ListParser::NewProvides - Create a Provides element /*{{{*/
524// ---------------------------------------------------------------------
525/* */
526bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver,
8efa2a3b 527 string PackageName,
9ddf7030 528 string Version)
dcb79bae
AL
529{
530 pkgCache &Cache = Owner->Cache;
8efa2a3b
AL
531
532 // We do not add self referencing provides
533 if (Ver.ParentPkg().Name() == PackageName)
534 return true;
dcb79bae
AL
535
536 // Get a structure
537 unsigned long Provides = Owner->Map.Allocate(sizeof(pkgCache::Provides));
538 if (Provides == 0)
539 return false;
a7e66b17 540 Cache.HeaderP->ProvidesCount++;
dcb79bae
AL
541
542 // Fill it in
543 pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
544 Prv->Version = Ver.Index();
545 Prv->NextPkgProv = Ver->ProvidesList;
546 Ver->ProvidesList = Prv.Index();
b2e465d6 547 if (Version.empty() == false && (Prv->ProvideVersion = WriteString(Version)) == 0)
dcb79bae
AL
548 return false;
549
550 // Locate the target package
8efa2a3b
AL
551 pkgCache::PkgIterator Pkg;
552 if (Owner->NewPackage(Pkg,PackageName) == false)
553 return false;
dcb79bae
AL
554
555 // Link it to the package
556 Prv->ParentPkg = Pkg.Index();
557 Prv->NextProvides = Pkg->ProvidesList;
558 Pkg->ProvidesList = Prv.Index();
559
560 return true;
561}
562 /*}}}*/
578bfd0a
AL
563// CacheGenerator::SelectFile - Select the current file being parsed /*{{{*/
564// ---------------------------------------------------------------------
565/* This is used to select which file is to be associated with all newly
b2e465d6
AL
566 added versions. The caller is responsible for setting the IMS fields. */
567bool pkgCacheGenerator::SelectFile(string File,string Site,
568 const pkgIndexFile &Index,
569 unsigned long Flags)
578bfd0a 570{
578bfd0a
AL
571 // Get some space for the structure
572 CurrentFile = Cache.PkgFileP + Map.Allocate(sizeof(*CurrentFile));
573 if (CurrentFile == Cache.PkgFileP)
574 return false;
575
576 // Fill it in
577 CurrentFile->FileName = Map.WriteString(File);
b2e465d6 578 CurrentFile->Site = WriteUniqString(Site);
578bfd0a
AL
579 CurrentFile->NextFile = Cache.HeaderP->FileList;
580 CurrentFile->Flags = Flags;
e1b74f61 581 CurrentFile->ID = Cache.HeaderP->PackageFileCount;
b2e465d6 582 CurrentFile->IndexType = WriteUniqString(Index.GetType()->Label);
578bfd0a 583 PkgFileName = File;
ad00ae81 584 Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
b35d2f5f 585 Cache.HeaderP->PackageFileCount++;
b2e465d6 586
578bfd0a
AL
587 if (CurrentFile->FileName == 0)
588 return false;
404ec98e 589
ddc1d8d0 590 if (Progress != 0)
b2e465d6 591 Progress->SubProgress(Index.Size());
8efa2a3b 592 return true;
578bfd0a
AL
593}
594 /*}}}*/
f55a958f
AL
595// CacheGenerator::WriteUniqueString - Insert a unique string /*{{{*/
596// ---------------------------------------------------------------------
597/* This is used to create handles to strings. Given the same text it
598 always returns the same number */
599unsigned long pkgCacheGenerator::WriteUniqString(const char *S,
600 unsigned int Size)
601{
f9eec0e7
AL
602 /* We use a very small transient hash table here, this speeds up generation
603 by a fair amount on slower machines */
604 pkgCache::StringItem *&Bucket = UniqHash[(S[0]*5 + S[1]) % _count(UniqHash)];
605 if (Bucket != 0 &&
606 stringcmp(S,S+Size,Cache.StrP + Bucket->String) == 0)
607 return Bucket->String;
608
f55a958f
AL
609 // Search for an insertion point
610 pkgCache::StringItem *I = Cache.StringItemP + Cache.HeaderP->StringList;
611 int Res = 1;
349cd3b8 612 map_ptrloc *Last = &Cache.HeaderP->StringList;
f55a958f
AL
613 for (; I != Cache.StringItemP; Last = &I->NextItem,
614 I = Cache.StringItemP + I->NextItem)
615 {
9c14e3d6 616 Res = stringcmp(S,S+Size,Cache.StrP + I->String);
f55a958f
AL
617 if (Res >= 0)
618 break;
619 }
620
621 // Match
622 if (Res == 0)
f9eec0e7
AL
623 {
624 Bucket = I;
0149949b 625 return I->String;
f9eec0e7 626 }
f55a958f
AL
627
628 // Get a structure
629 unsigned long Item = Map.Allocate(sizeof(pkgCache::StringItem));
630 if (Item == 0)
0149949b
AL
631 return 0;
632
f55a958f
AL
633 // Fill in the structure
634 pkgCache::StringItem *ItemP = Cache.StringItemP + Item;
635 ItemP->NextItem = I - Cache.StringItemP;
636 *Last = Item;
637 ItemP->String = Map.WriteString(S,Size);
638 if (ItemP->String == 0)
0149949b 639 return 0;
f55a958f 640
f9eec0e7 641 Bucket = ItemP;
0149949b 642 return ItemP->String;
f55a958f
AL
643}
644 /*}}}*/
b35d2f5f 645
b2e465d6 646// CheckValidity - Check that a cache is up-to-date /*{{{*/
b35d2f5f 647// ---------------------------------------------------------------------
b2e465d6
AL
648/* This just verifies that each file in the list of index files exists,
649 has matching attributes with the cache and the cache does not have
650 any extra files. */
e7b470ee
AL
651static bool CheckValidity(string CacheFile, FileIterator Start,
652 FileIterator End,MMap **OutMap = 0)
b35d2f5f 653{
b2e465d6
AL
654 // No file, certainly invalid
655 if (CacheFile.empty() == true || FileExists(CacheFile) == false)
b35d2f5f
AL
656 return false;
657
b2e465d6 658 // Map it
b35d2f5f 659 FileFd CacheF(CacheFile,FileFd::ReadOnly);
b2e465d6 660 SPtr<MMap> Map = new MMap(CacheF,MMap::Public | MMap::ReadOnly);
b35d2f5f 661 pkgCache Cache(Map);
b2e465d6 662 if (_error->PendingError() == true || Map->Size() == 0)
b35d2f5f
AL
663 {
664 _error->Discard();
665 return false;
666 }
b35d2f5f 667
b2e465d6
AL
668 /* Now we check every index file, see if it is in the cache,
669 verify the IMS data and check that it is on the disk too.. */
670 SPtrArray<bool> Visited = new bool[Cache.HeaderP->PackageFileCount];
671 memset(Visited,0,sizeof(*Visited)*Cache.HeaderP->PackageFileCount);
672 for (; Start != End; Start++)
a77ad7c3 673 {
b2e465d6
AL
674 if ((*Start)->HasPackages() == false)
675 continue;
a77ad7c3 676
b2e465d6 677 if ((*Start)->Exists() == false)
b35d2f5f 678 {
b2e465d6
AL
679 _error->WarningE("stat",_("Couldn't stat source package list %s"),
680 (*Start)->Describe().c_str());
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{
ac13a427 784 unsigned long MapSize = _config->FindI("APT::Cache-Limit",12*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{
ac13a427 931 unsigned long MapSize = _config->FindI("APT::Cache-Limit",8*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 /*}}}*/