]> git.saurik.com Git - apt.git/blob - apt-pkg/pkgcachegen.cc
3cbb96a6b49fdd7785feefa4f4696ef4366abdea
[apt.git] / apt-pkg / pkgcachegen.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: pkgcachegen.cc,v 1.53.2.1 2003/12/24 23:09:17 mdz Exp $
4 /* ######################################################################
5
6 Package Cache Generator - Generator for the cache structure.
7
8 This builds the cache structure from the abstract package list parser.
9
10 ##################################################################### */
11 /*}}}*/
12 // Include Files /*{{{*/
13 #include <config.h>
14
15 #include <apt-pkg/pkgcachegen.h>
16 #include <apt-pkg/error.h>
17 #include <apt-pkg/version.h>
18 #include <apt-pkg/progress.h>
19 #include <apt-pkg/sourcelist.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/pkgsystem.h>
22 #include <apt-pkg/macros.h>
23 #include <apt-pkg/metaindex.h>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/hashsum_template.h>
26 #include <apt-pkg/indexfile.h>
27 #include <apt-pkg/md5.h>
28 #include <apt-pkg/mmap.h>
29 #include <apt-pkg/pkgcache.h>
30 #include <apt-pkg/cacheiterators.h>
31
32 #include <stddef.h>
33 #include <string.h>
34 #include <iostream>
35 #include <string>
36 #include <vector>
37 #include <memory>
38 #include <algorithm>
39 #include <sys/stat.h>
40 #include <unistd.h>
41
42 #include <apti18n.h>
43
44 template<class T> using Dynamic = pkgCacheGenerator::Dynamic<T>; /*}}}*/
45 typedef std::vector<pkgIndexFile *>::iterator FileIterator;
46 template <typename Iter> std::vector<Iter*> pkgCacheGenerator::Dynamic<Iter>::toReMap;
47
48 static bool IsDuplicateDescription(pkgCache::DescIterator Desc,
49 MD5SumValue const &CurMd5, std::string const &CurLang);
50
51 using std::string;
52
53 // CacheGenerator::pkgCacheGenerator - Constructor /*{{{*/
54 // ---------------------------------------------------------------------
55 /* We set the dirty flag and make sure that is written to the disk */
56 pkgCacheGenerator::pkgCacheGenerator(DynamicMMap *pMap,OpProgress *Prog) :
57 Map(*pMap), Cache(pMap,false), Progress(Prog),
58 CurrentRlsFile(NULL), CurrentFile(NULL), d(NULL)
59 {
60 if (_error->PendingError() == true)
61 return;
62
63 if (Map.Size() == 0)
64 {
65 // Setup the map interface..
66 Cache.HeaderP = (pkgCache::Header *)Map.Data();
67 if (Map.RawAllocate(sizeof(pkgCache::Header)) == 0 && _error->PendingError() == true)
68 return;
69
70 Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
71
72 // Starting header
73 *Cache.HeaderP = pkgCache::Header();
74
75 // make room for the hashtables for packages and groups
76 if (Map.RawAllocate(2 * (Cache.HeaderP->GetHashTableSize() * sizeof(map_pointer_t))) == 0)
77 return;
78
79 map_stringitem_t const idxVerSysName = WriteStringInMap(_system->VS->Label);
80 if (unlikely(idxVerSysName == 0))
81 return;
82 Cache.HeaderP->VerSysName = idxVerSysName;
83 map_stringitem_t const idxArchitecture = StoreString(MIXED, _config->Find("APT::Architecture"));
84 if (unlikely(idxArchitecture == 0))
85 return;
86 Cache.HeaderP->Architecture = idxArchitecture;
87
88 std::vector<std::string> archs = APT::Configuration::getArchitectures();
89 if (archs.size() > 1)
90 {
91 std::vector<std::string>::const_iterator a = archs.begin();
92 std::string list = *a;
93 for (++a; a != archs.end(); ++a)
94 list.append(",").append(*a);
95 map_stringitem_t const idxArchitectures = WriteStringInMap(list);
96 if (unlikely(idxArchitectures == 0))
97 return;
98 Cache.HeaderP->SetArchitectures(idxArchitectures);
99 }
100 else
101 Cache.HeaderP->SetArchitectures(idxArchitecture);
102
103 Cache.ReMap();
104 }
105 else
106 {
107 // Map directly from the existing file
108 Cache.ReMap();
109 Map.UsePools(*Cache.HeaderP->Pools,sizeof(Cache.HeaderP->Pools)/sizeof(Cache.HeaderP->Pools[0]));
110 if (Cache.VS != _system->VS)
111 {
112 _error->Error(_("Cache has an incompatible versioning system"));
113 return;
114 }
115 }
116
117 Cache.HeaderP->Dirty = true;
118 Map.Sync(0,sizeof(pkgCache::Header));
119 }
120 /*}}}*/
121 // CacheGenerator::~pkgCacheGenerator - Destructor /*{{{*/
122 // ---------------------------------------------------------------------
123 /* We sync the data then unset the dirty flag in two steps so as to
124 advoid a problem during a crash */
125 pkgCacheGenerator::~pkgCacheGenerator()
126 {
127 if (_error->PendingError() == true)
128 return;
129 if (Map.Sync() == false)
130 return;
131
132 Cache.HeaderP->Dirty = false;
133 Cache.HeaderP->CacheFileSize = Map.Size();
134 Map.Sync(0,sizeof(pkgCache::Header));
135 }
136 /*}}}*/
137 void pkgCacheGenerator::ReMap(void const * const oldMap, void const * const newMap) {/*{{{*/
138 if (oldMap == newMap)
139 return;
140
141 if (_config->FindB("Debug::pkgCacheGen", false))
142 std::clog << "Remaping from " << oldMap << " to " << newMap << std::endl;
143
144 Cache.ReMap(false);
145
146 CurrentFile += (pkgCache::PackageFile const * const) newMap - (pkgCache::PackageFile const * const) oldMap;
147 CurrentRlsFile += (pkgCache::ReleaseFile const * const) newMap - (pkgCache::ReleaseFile const * const) oldMap;
148
149 for (std::vector<pkgCache::GrpIterator*>::const_iterator i = Dynamic<pkgCache::GrpIterator>::toReMap.begin();
150 i != Dynamic<pkgCache::GrpIterator>::toReMap.end(); ++i)
151 (*i)->ReMap(oldMap, newMap);
152 for (std::vector<pkgCache::PkgIterator*>::const_iterator i = Dynamic<pkgCache::PkgIterator>::toReMap.begin();
153 i != Dynamic<pkgCache::PkgIterator>::toReMap.end(); ++i)
154 (*i)->ReMap(oldMap, newMap);
155 for (std::vector<pkgCache::VerIterator*>::const_iterator i = Dynamic<pkgCache::VerIterator>::toReMap.begin();
156 i != Dynamic<pkgCache::VerIterator>::toReMap.end(); ++i)
157 (*i)->ReMap(oldMap, newMap);
158 for (std::vector<pkgCache::DepIterator*>::const_iterator i = Dynamic<pkgCache::DepIterator>::toReMap.begin();
159 i != Dynamic<pkgCache::DepIterator>::toReMap.end(); ++i)
160 (*i)->ReMap(oldMap, newMap);
161 for (std::vector<pkgCache::DescIterator*>::const_iterator i = Dynamic<pkgCache::DescIterator>::toReMap.begin();
162 i != Dynamic<pkgCache::DescIterator>::toReMap.end(); ++i)
163 (*i)->ReMap(oldMap, newMap);
164 for (std::vector<pkgCache::PrvIterator*>::const_iterator i = Dynamic<pkgCache::PrvIterator>::toReMap.begin();
165 i != Dynamic<pkgCache::PrvIterator>::toReMap.end(); ++i)
166 (*i)->ReMap(oldMap, newMap);
167 for (std::vector<pkgCache::PkgFileIterator*>::const_iterator i = Dynamic<pkgCache::PkgFileIterator>::toReMap.begin();
168 i != Dynamic<pkgCache::PkgFileIterator>::toReMap.end(); ++i)
169 (*i)->ReMap(oldMap, newMap);
170 for (std::vector<pkgCache::RlsFileIterator*>::const_iterator i = Dynamic<pkgCache::RlsFileIterator>::toReMap.begin();
171 i != Dynamic<pkgCache::RlsFileIterator>::toReMap.end(); ++i)
172 (*i)->ReMap(oldMap, newMap);
173 } /*}}}*/
174 // CacheGenerator::WriteStringInMap /*{{{*/
175 map_stringitem_t pkgCacheGenerator::WriteStringInMap(const char *String,
176 const unsigned long &Len) {
177 void const * const oldMap = Map.Data();
178 map_stringitem_t const index = Map.WriteString(String, Len);
179 if (index != 0)
180 ReMap(oldMap, Map.Data());
181 return index;
182 }
183 /*}}}*/
184 // CacheGenerator::WriteStringInMap /*{{{*/
185 map_stringitem_t pkgCacheGenerator::WriteStringInMap(const char *String) {
186 void const * const oldMap = Map.Data();
187 map_stringitem_t const index = Map.WriteString(String);
188 if (index != 0)
189 ReMap(oldMap, Map.Data());
190 return index;
191 }
192 /*}}}*/
193 map_pointer_t pkgCacheGenerator::AllocateInMap(const unsigned long &size) {/*{{{*/
194 void const * const oldMap = Map.Data();
195 map_pointer_t const index = Map.Allocate(size);
196 if (index != 0)
197 ReMap(oldMap, Map.Data());
198 return index;
199 }
200 /*}}}*/
201 // CacheGenerator::MergeList - Merge the package list /*{{{*/
202 // ---------------------------------------------------------------------
203 /* This provides the generation of the entries in the cache. Each loop
204 goes through a single package record from the underlying parse engine. */
205 bool pkgCacheGenerator::MergeList(ListParser &List,
206 pkgCache::VerIterator *OutVer)
207 {
208 List.Owner = this;
209
210 unsigned int Counter = 0;
211 while (List.Step() == true)
212 {
213 string const PackageName = List.Package();
214 if (PackageName.empty() == true)
215 return false;
216
217 Counter++;
218 if (Counter % 100 == 0 && Progress != 0)
219 Progress->Progress(List.Offset());
220
221 string Arch = List.Architecture();
222 string const Version = List.Version();
223 if (Version.empty() == true && Arch.empty() == true)
224 {
225 // package descriptions
226 if (MergeListGroup(List, PackageName) == false)
227 return false;
228 continue;
229 }
230
231 // Get a pointer to the package structure
232 pkgCache::PkgIterator Pkg;
233 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
234 if (NewPackage(Pkg, PackageName, Arch) == false)
235 // TRANSLATOR: The first placeholder is a package name,
236 // the other two should be copied verbatim as they include debug info
237 return _error->Error(_("Error occurred while processing %s (%s%d)"),
238 PackageName.c_str(), "NewPackage", 1);
239
240
241 if (Version.empty() == true)
242 {
243 if (MergeListPackage(List, Pkg) == false)
244 return false;
245 }
246 else
247 {
248 if (MergeListVersion(List, Pkg, Version, OutVer) == false)
249 return false;
250 }
251
252 if (OutVer != 0)
253 return true;
254 }
255
256 if (Cache.HeaderP->PackageCount >= std::numeric_limits<map_id_t>::max())
257 return _error->Error(_("Wow, you exceeded the number of package "
258 "names this APT is capable of."));
259 if (Cache.HeaderP->VersionCount >= std::numeric_limits<map_id_t>::max())
260 return _error->Error(_("Wow, you exceeded the number of versions "
261 "this APT is capable of."));
262 if (Cache.HeaderP->DescriptionCount >= std::numeric_limits<map_id_t>::max())
263 return _error->Error(_("Wow, you exceeded the number of descriptions "
264 "this APT is capable of."));
265 if (Cache.HeaderP->DependsCount >= std::numeric_limits<map_id_t>::max())
266 return _error->Error(_("Wow, you exceeded the number of dependencies "
267 "this APT is capable of."));
268
269 return true;
270 }
271 // CacheGenerator::MergeListGroup /*{{{*/
272 bool pkgCacheGenerator::MergeListGroup(ListParser &List, std::string const &GrpName)
273 {
274 pkgCache::GrpIterator Grp = Cache.FindGrp(GrpName);
275 // a group has no data on it's own, only packages have it but these
276 // stanzas like this come from Translation- files to add descriptions,
277 // but without a version we don't need a description for it…
278 if (Grp.end() == true)
279 return true;
280 Dynamic<pkgCache::GrpIterator> DynGrp(Grp);
281
282 pkgCache::PkgIterator Pkg;
283 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
284 for (Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
285 if (MergeListPackage(List, Pkg) == false)
286 return false;
287
288 return true;
289 }
290 /*}}}*/
291 // CacheGenerator::MergeListPackage /*{{{*/
292 bool pkgCacheGenerator::MergeListPackage(ListParser &List, pkgCache::PkgIterator &Pkg)
293 {
294 // we first process the package, then the descriptions
295 // (for deb this package processing is in fact a no-op)
296 pkgCache::VerIterator Ver(Cache);
297 Dynamic<pkgCache::VerIterator> DynVer(Ver);
298 if (List.UsePackage(Pkg, Ver) == false)
299 return _error->Error(_("Error occurred while processing %s (%s%d)"),
300 Pkg.Name(), "UsePackage", 1);
301
302 // Find the right version to write the description
303 MD5SumValue CurMd5 = List.Description_md5();
304 if (CurMd5.Value().empty() == true && List.Description("").empty() == true)
305 return true;
306 std::vector<std::string> availDesc = List.AvailableDescriptionLanguages();
307 for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
308 {
309 pkgCache::DescIterator VerDesc = Ver.DescriptionList();
310
311 // a version can only have one md5 describing it
312 if (VerDesc.end() == true || MD5SumValue(VerDesc.md5()) != CurMd5)
313 continue;
314
315 map_stringitem_t md5idx = VerDesc->md5sum;
316 for (std::vector<std::string>::const_iterator CurLang = availDesc.begin(); CurLang != availDesc.end(); ++CurLang)
317 {
318 // don't add a new description if we have one for the given
319 // md5 && language
320 if (IsDuplicateDescription(VerDesc, CurMd5, *CurLang) == true)
321 continue;
322
323 AddNewDescription(List, Ver, *CurLang, CurMd5, md5idx);
324 }
325
326 // we can stop here as all "same" versions will share the description
327 break;
328 }
329
330 return true;
331 }
332 /*}}}*/
333 // CacheGenerator::MergeListVersion /*{{{*/
334 bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator &Pkg,
335 std::string const &Version, pkgCache::VerIterator* &OutVer)
336 {
337 pkgCache::VerIterator Ver = Pkg.VersionList();
338 Dynamic<pkgCache::VerIterator> DynVer(Ver);
339 map_pointer_t *LastVer = &Pkg->VersionList;
340 void const * oldMap = Map.Data();
341
342 unsigned short const Hash = List.VersionHash();
343 if (Ver.end() == false)
344 {
345 /* We know the list is sorted so we use that fact in the search.
346 Insertion of new versions is done with correct sorting */
347 int Res = 1;
348 for (; Ver.end() == false; LastVer = &Ver->NextVer, ++Ver)
349 {
350 Res = Cache.VS->CmpVersion(Version,Ver.VerStr());
351 // Version is higher as current version - insert here
352 if (Res > 0)
353 break;
354 // Versionstrings are equal - is hash also equal?
355 if (Res == 0 && List.SameVersion(Hash, Ver) == true)
356 break;
357 // proceed with the next till we have either the right
358 // or we found another version (which will be lower)
359 }
360
361 /* We already have a version for this item, record that we saw it */
362 if (Res == 0 && Ver.end() == false && Ver->Hash == Hash)
363 {
364 if (List.UsePackage(Pkg,Ver) == false)
365 return _error->Error(_("Error occurred while processing %s (%s%d)"),
366 Pkg.Name(), "UsePackage", 2);
367
368 if (NewFileVer(Ver,List) == false)
369 return _error->Error(_("Error occurred while processing %s (%s%d)"),
370 Pkg.Name(), "NewFileVer", 1);
371
372 // Read only a single record and return
373 if (OutVer != 0)
374 {
375 *OutVer = Ver;
376 return true;
377 }
378
379 return true;
380 }
381 }
382
383 // Add a new version
384 map_pointer_t const verindex = NewVersion(Ver, Version, Pkg.Index(), Hash, *LastVer);
385 if (verindex == 0 && _error->PendingError())
386 return _error->Error(_("Error occurred while processing %s (%s%d)"),
387 Pkg.Name(), "NewVersion", 1);
388
389 if (oldMap != Map.Data())
390 LastVer += (map_pointer_t const * const) Map.Data() - (map_pointer_t const * const) oldMap;
391 *LastVer = verindex;
392
393 if (unlikely(List.NewVersion(Ver) == false))
394 return _error->Error(_("Error occurred while processing %s (%s%d)"),
395 Pkg.Name(), "NewVersion", 2);
396
397 if (unlikely(List.UsePackage(Pkg,Ver) == false))
398 return _error->Error(_("Error occurred while processing %s (%s%d)"),
399 Pkg.Name(), "UsePackage", 3);
400
401 if (unlikely(NewFileVer(Ver,List) == false))
402 return _error->Error(_("Error occurred while processing %s (%s%d)"),
403 Pkg.Name(), "NewFileVer", 2);
404
405 pkgCache::GrpIterator Grp = Pkg.Group();
406 Dynamic<pkgCache::GrpIterator> DynGrp(Grp);
407
408 /* If it is the first version of this package we need to add implicit
409 Multi-Arch dependencies to all other package versions in the group now -
410 otherwise we just add them for this new version */
411 if (Pkg.VersionList()->NextVer == 0)
412 {
413 pkgCache::PkgIterator P = Grp.PackageList();
414 Dynamic<pkgCache::PkgIterator> DynP(P);
415 for (; P.end() != true; P = Grp.NextPkg(P))
416 {
417 if (P->ID == Pkg->ID)
418 continue;
419 pkgCache::VerIterator V = P.VersionList();
420 Dynamic<pkgCache::VerIterator> DynV(V);
421 for (; V.end() != true; ++V)
422 if (unlikely(AddImplicitDepends(V, Pkg) == false))
423 return _error->Error(_("Error occurred while processing %s (%s%d)"),
424 Pkg.Name(), "AddImplicitDepends", 1);
425 }
426 }
427 if (unlikely(AddImplicitDepends(Grp, Pkg, Ver) == false))
428 return _error->Error(_("Error occurred while processing %s (%s%d)"),
429 Pkg.Name(), "AddImplicitDepends", 2);
430
431 // Read only a single record and return
432 if (OutVer != 0)
433 {
434 *OutVer = Ver;
435 return true;
436 }
437
438 /* Record the Description(s) based on their master md5sum */
439 MD5SumValue CurMd5 = List.Description_md5();
440 if (CurMd5.Value().empty() == true && List.Description("").empty() == true)
441 return true;
442
443 /* Before we add a new description we first search in the group for
444 a version with a description of the same MD5 - if so we reuse this
445 description group instead of creating our own for this version */
446 for (pkgCache::PkgIterator P = Grp.PackageList();
447 P.end() == false; P = Grp.NextPkg(P))
448 {
449 for (pkgCache::VerIterator V = P.VersionList();
450 V.end() == false; ++V)
451 {
452 if (V->DescriptionList == 0 || MD5SumValue(V.DescriptionList().md5()) != CurMd5)
453 continue;
454 Ver->DescriptionList = V->DescriptionList;
455 }
456 }
457
458 // We haven't found reusable descriptions, so add the first description(s)
459 map_stringitem_t md5idx = Ver->DescriptionList == 0 ? 0 : Ver.DescriptionList()->md5sum;
460 std::vector<std::string> availDesc = List.AvailableDescriptionLanguages();
461 for (std::vector<std::string>::const_iterator CurLang = availDesc.begin(); CurLang != availDesc.end(); ++CurLang)
462 if (AddNewDescription(List, Ver, *CurLang, CurMd5, md5idx) == false)
463 return false;
464 return true;
465 }
466 /*}}}*/
467 bool pkgCacheGenerator::AddNewDescription(ListParser &List, pkgCache::VerIterator &Ver, std::string const &lang, MD5SumValue const &CurMd5, map_stringitem_t &md5idx) /*{{{*/
468 {
469 pkgCache::DescIterator Desc;
470 Dynamic<pkgCache::DescIterator> DynDesc(Desc);
471
472 map_pointer_t const descindex = NewDescription(Desc, lang, CurMd5, md5idx);
473 if (unlikely(descindex == 0 && _error->PendingError()))
474 return _error->Error(_("Error occurred while processing %s (%s%d)"),
475 Ver.ParentPkg().Name(), "NewDescription", 1);
476
477 md5idx = Desc->md5sum;
478 Desc->ParentPkg = Ver.ParentPkg().Index();
479
480 // we add at the end, so that the start is constant as we need
481 // that to be able to efficiently share these lists
482 pkgCache::DescIterator VerDesc = Ver.DescriptionList(); // old value might be invalid after ReMap
483 for (;VerDesc.end() == false && VerDesc->NextDesc != 0; ++VerDesc);
484 map_pointer_t * const LastNextDesc = (VerDesc.end() == true) ? &Ver->DescriptionList : &VerDesc->NextDesc;
485 *LastNextDesc = descindex;
486
487 if (NewFileDesc(Desc,List) == false)
488 return _error->Error(_("Error occurred while processing %s (%s%d)"),
489 Ver.ParentPkg().Name(), "NewFileDesc", 1);
490
491 return true;
492 }
493 /*}}}*/
494 /*}}}*/
495 // CacheGenerator::NewGroup - Add a new group /*{{{*/
496 // ---------------------------------------------------------------------
497 /* This creates a new group structure and adds it to the hash table */
498 bool pkgCacheGenerator::NewGroup(pkgCache::GrpIterator &Grp, const string &Name)
499 {
500 Grp = Cache.FindGrp(Name);
501 if (Grp.end() == false)
502 return true;
503
504 // Get a structure
505 map_pointer_t const Group = AllocateInMap(sizeof(pkgCache::Group));
506 if (unlikely(Group == 0))
507 return false;
508
509 Grp = pkgCache::GrpIterator(Cache, Cache.GrpP + Group);
510 map_stringitem_t const idxName = StoreString(PKGNAME, Name);
511 if (unlikely(idxName == 0))
512 return false;
513 Grp->Name = idxName;
514
515 // Insert it into the hash table
516 unsigned long const Hash = Cache.Hash(Name);
517 map_pointer_t *insertAt = &Cache.HeaderP->GrpHashTableP()[Hash];
518 while (*insertAt != 0 && strcasecmp(Name.c_str(), Cache.StrP + (Cache.GrpP + *insertAt)->Name) > 0)
519 insertAt = &(Cache.GrpP + *insertAt)->Next;
520 Grp->Next = *insertAt;
521 *insertAt = Group;
522
523 Grp->ID = Cache.HeaderP->GroupCount++;
524 return true;
525 }
526 /*}}}*/
527 // CacheGenerator::NewPackage - Add a new package /*{{{*/
528 // ---------------------------------------------------------------------
529 /* This creates a new package structure and adds it to the hash table */
530 bool pkgCacheGenerator::NewPackage(pkgCache::PkgIterator &Pkg,const string &Name,
531 const string &Arch) {
532 pkgCache::GrpIterator Grp;
533 Dynamic<pkgCache::GrpIterator> DynGrp(Grp);
534 if (unlikely(NewGroup(Grp, Name) == false))
535 return false;
536
537 Pkg = Grp.FindPkg(Arch);
538 if (Pkg.end() == false)
539 return true;
540
541 // Get a structure
542 map_pointer_t const Package = AllocateInMap(sizeof(pkgCache::Package));
543 if (unlikely(Package == 0))
544 return false;
545 Pkg = pkgCache::PkgIterator(Cache,Cache.PkgP + Package);
546
547 // Set the name, arch and the ID
548 APT_IGNORE_DEPRECATED(Pkg->Name = Grp->Name;)
549 Pkg->Group = Grp.Index();
550 // all is mapped to the native architecture
551 map_stringitem_t const idxArch = (Arch == "all") ? Cache.HeaderP->Architecture : StoreString(MIXED, Arch);
552 if (unlikely(idxArch == 0))
553 return false;
554 Pkg->Arch = idxArch;
555 Pkg->ID = Cache.HeaderP->PackageCount++;
556
557 // Insert the package into our package list
558 if (Grp->FirstPackage == 0) // the group is new
559 {
560 Grp->FirstPackage = Package;
561 // Insert it into the hash table
562 map_id_t const Hash = Cache.Hash(Name);
563 map_pointer_t *insertAt = &Cache.HeaderP->PkgHashTableP()[Hash];
564 while (*insertAt != 0 && strcasecmp(Name.c_str(), Cache.StrP + (Cache.GrpP + (Cache.PkgP + *insertAt)->Group)->Name) > 0)
565 insertAt = &(Cache.PkgP + *insertAt)->NextPackage;
566 Pkg->NextPackage = *insertAt;
567 *insertAt = Package;
568 }
569 else // Group the Packages together
570 {
571 // but first get implicit provides done
572 if (APT::Configuration::checkArchitecture(Pkg.Arch()) == true)
573 {
574 pkgCache::PkgIterator const M = Grp.FindPreferredPkg(false); // native or any foreign pkg will do
575 if (M.end() == false)
576 for (pkgCache::PrvIterator Prv = M.ProvidesList(); Prv.end() == false; ++Prv)
577 {
578 if ((Prv->Flags & pkgCache::Flag::ArchSpecific) != 0)
579 continue;
580 pkgCache::VerIterator Ver = Prv.OwnerVer();
581 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed ||
582 ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign &&
583 (Prv->Flags & pkgCache::Flag::MultiArchImplicit) == 0))
584 if (NewProvides(Ver, Pkg, Prv->ProvideVersion, Prv->Flags) == false)
585 return false;
586 }
587
588 for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
589 for (pkgCache::VerIterator Ver = P.VersionList(); Ver.end() == false; ++Ver)
590 if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
591 if (NewProvides(Ver, Pkg, Ver->VerStr, pkgCache::Flag::MultiArchImplicit) == false)
592 return false;
593 }
594 // and negative dependencies, don't forget negative dependencies
595 {
596 pkgCache::PkgIterator const M = Grp.FindPreferredPkg(false);
597 if (M.end() == false) {
598 pkgCache::DepIterator Dep;
599 Dynamic<pkgCache::DepIterator> DynDep(Dep);
600 for (Dep = M.RevDependsList(); Dep.end() == false; ++Dep)
601 {
602 if ((Dep->CompareOp & (pkgCache::Dep::ArchSpecific | pkgCache::Dep::MultiArchImplicit)) != 0)
603 continue;
604 if (Dep->Type != pkgCache::Dep::DpkgBreaks && Dep->Type != pkgCache::Dep::Conflicts &&
605 Dep->Type != pkgCache::Dep::Replaces)
606 continue;
607 pkgCache::VerIterator Ver = Dep.ParentVer();
608 Dynamic<pkgCache::VerIterator> DynVer(Ver);
609 map_pointer_t * unused = NULL;
610 if (NewDepends(Pkg, Ver, Dep->Version, Dep->CompareOp, Dep->Type, unused) == false)
611 return false;
612 }
613 }
614 }
615
616 // this package is the new last package
617 pkgCache::PkgIterator LastPkg(Cache, Cache.PkgP + Grp->LastPackage);
618 Pkg->NextPackage = LastPkg->NextPackage;
619 LastPkg->NextPackage = Package;
620 }
621 Grp->LastPackage = Package;
622 return true;
623 }
624 /*}}}*/
625 // CacheGenerator::AddImplicitDepends /*{{{*/
626 bool pkgCacheGenerator::AddImplicitDepends(pkgCache::GrpIterator &G,
627 pkgCache::PkgIterator &P,
628 pkgCache::VerIterator &V)
629 {
630 // copy P.Arch() into a string here as a cache remap
631 // in NewDepends() later may alter the pointer location
632 string Arch = P.Arch() == NULL ? "" : P.Arch();
633 map_pointer_t *OldDepLast = NULL;
634 /* MultiArch handling introduces a lot of implicit Dependencies:
635 - MultiArch: same → Co-Installable if they have the same version
636 - All others conflict with all other group members */
637 bool const coInstall = ((V->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same);
638 pkgCache::PkgIterator D = G.PackageList();
639 Dynamic<pkgCache::PkgIterator> DynD(D);
640 map_stringitem_t const VerStrIdx = V->VerStr;
641 for (; D.end() != true; D = G.NextPkg(D))
642 {
643 if (Arch == D.Arch() || D->VersionList == 0)
644 continue;
645 /* We allow only one installed arch at the time
646 per group, therefore each group member conflicts
647 with all other group members */
648 if (coInstall == true)
649 {
650 // Replaces: ${self}:other ( << ${binary:Version})
651 NewDepends(D, V, VerStrIdx,
652 pkgCache::Dep::Less | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Replaces,
653 OldDepLast);
654 // Breaks: ${self}:other (!= ${binary:Version})
655 NewDepends(D, V, VerStrIdx,
656 pkgCache::Dep::NotEquals | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::DpkgBreaks,
657 OldDepLast);
658 } else {
659 // Conflicts: ${self}:other
660 NewDepends(D, V, 0,
661 pkgCache::Dep::NoOp | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Conflicts,
662 OldDepLast);
663 }
664 }
665 return true;
666 }
667 bool pkgCacheGenerator::AddImplicitDepends(pkgCache::VerIterator &V,
668 pkgCache::PkgIterator &D)
669 {
670 /* MultiArch handling introduces a lot of implicit Dependencies:
671 - MultiArch: same → Co-Installable if they have the same version
672 - All others conflict with all other group members */
673 map_pointer_t *OldDepLast = NULL;
674 bool const coInstall = ((V->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same);
675 if (coInstall == true)
676 {
677 map_stringitem_t const VerStrIdx = V->VerStr;
678 // Replaces: ${self}:other ( << ${binary:Version})
679 NewDepends(D, V, VerStrIdx,
680 pkgCache::Dep::Less | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Replaces,
681 OldDepLast);
682 // Breaks: ${self}:other (!= ${binary:Version})
683 NewDepends(D, V, VerStrIdx,
684 pkgCache::Dep::NotEquals | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::DpkgBreaks,
685 OldDepLast);
686 } else {
687 // Conflicts: ${self}:other
688 NewDepends(D, V, 0,
689 pkgCache::Dep::NoOp | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Conflicts,
690 OldDepLast);
691 }
692 return true;
693 }
694
695 /*}}}*/
696 // CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
697 // ---------------------------------------------------------------------
698 /* */
699 bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver,
700 ListParser &List)
701 {
702 if (CurrentFile == 0)
703 return true;
704
705 // Get a structure
706 map_pointer_t const VerFile = AllocateInMap(sizeof(pkgCache::VerFile));
707 if (VerFile == 0)
708 return false;
709
710 pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile);
711 VF->File = CurrentFile - Cache.PkgFileP;
712
713 // Link it to the end of the list
714 map_pointer_t *Last = &Ver->FileList;
715 for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; ++V)
716 Last = &V->NextFile;
717 VF->NextFile = *Last;
718 *Last = VF.Index();
719
720 VF->Offset = List.Offset();
721 VF->Size = List.Size();
722 if (Cache.HeaderP->MaxVerFileSize < VF->Size)
723 Cache.HeaderP->MaxVerFileSize = VF->Size;
724 Cache.HeaderP->VerFileCount++;
725
726 return true;
727 }
728 /*}}}*/
729 // CacheGenerator::NewVersion - Create a new Version /*{{{*/
730 // ---------------------------------------------------------------------
731 /* This puts a version structure in the linked list */
732 map_pointer_t pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver,
733 const string &VerStr,
734 map_pointer_t const ParentPkg,
735 unsigned short const Hash,
736 map_pointer_t const Next)
737 {
738 // Get a structure
739 map_pointer_t const Version = AllocateInMap(sizeof(pkgCache::Version));
740 if (Version == 0)
741 return 0;
742
743 // Fill it in
744 Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
745 //Dynamic<pkgCache::VerIterator> DynV(Ver); // caller MergeListVersion already takes care of it
746 Ver->NextVer = Next;
747 Ver->ParentPkg = ParentPkg;
748 Ver->Hash = Hash;
749 Ver->ID = Cache.HeaderP->VersionCount++;
750
751 // try to find the version string in the group for reuse
752 pkgCache::PkgIterator Pkg = Ver.ParentPkg();
753 pkgCache::GrpIterator Grp = Pkg.Group();
754 if (Pkg.end() == false && Grp.end() == false)
755 {
756 for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
757 {
758 if (Pkg == P)
759 continue;
760 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
761 {
762 int const cmp = strcmp(V.VerStr(), VerStr.c_str());
763 if (cmp == 0)
764 {
765 Ver->VerStr = V->VerStr;
766 return Version;
767 }
768 else if (cmp < 0)
769 break;
770 }
771 }
772 }
773 // haven't found the version string, so create
774 map_stringitem_t const idxVerStr = StoreString(VERSIONNUMBER, VerStr);
775 if (unlikely(idxVerStr == 0))
776 return 0;
777 Ver->VerStr = idxVerStr;
778 return Version;
779 }
780 /*}}}*/
781 // CacheGenerator::NewFileDesc - Create a new File<->Desc association /*{{{*/
782 // ---------------------------------------------------------------------
783 /* */
784 bool pkgCacheGenerator::NewFileDesc(pkgCache::DescIterator &Desc,
785 ListParser &List)
786 {
787 if (CurrentFile == 0)
788 return true;
789
790 // Get a structure
791 map_pointer_t const DescFile = AllocateInMap(sizeof(pkgCache::DescFile));
792 if (DescFile == 0)
793 return false;
794
795 pkgCache::DescFileIterator DF(Cache,Cache.DescFileP + DescFile);
796 DF->File = CurrentFile - Cache.PkgFileP;
797
798 // Link it to the end of the list
799 map_pointer_t *Last = &Desc->FileList;
800 for (pkgCache::DescFileIterator D = Desc.FileList(); D.end() == false; ++D)
801 Last = &D->NextFile;
802
803 DF->NextFile = *Last;
804 *Last = DF.Index();
805
806 DF->Offset = List.Offset();
807 DF->Size = List.Size();
808 if (Cache.HeaderP->MaxDescFileSize < DF->Size)
809 Cache.HeaderP->MaxDescFileSize = DF->Size;
810 Cache.HeaderP->DescFileCount++;
811
812 return true;
813 }
814 /*}}}*/
815 // CacheGenerator::NewDescription - Create a new Description /*{{{*/
816 // ---------------------------------------------------------------------
817 /* This puts a description structure in the linked list */
818 map_pointer_t pkgCacheGenerator::NewDescription(pkgCache::DescIterator &Desc,
819 const string &Lang,
820 const MD5SumValue &md5sum,
821 map_stringitem_t const idxmd5str)
822 {
823 // Get a structure
824 map_pointer_t const Description = AllocateInMap(sizeof(pkgCache::Description));
825 if (Description == 0)
826 return 0;
827
828 // Fill it in
829 Desc = pkgCache::DescIterator(Cache,Cache.DescP + Description);
830 Desc->ID = Cache.HeaderP->DescriptionCount++;
831 map_stringitem_t const idxlanguage_code = StoreString(MIXED, Lang);
832 if (unlikely(idxlanguage_code == 0))
833 return 0;
834 Desc->language_code = idxlanguage_code;
835
836 if (idxmd5str != 0)
837 Desc->md5sum = idxmd5str;
838 else
839 {
840 map_stringitem_t const idxmd5sum = WriteStringInMap(md5sum.Value());
841 if (unlikely(idxmd5sum == 0))
842 return 0;
843 Desc->md5sum = idxmd5sum;
844 }
845
846 return Description;
847 }
848 /*}}}*/
849 // CacheGenerator::NewDepends - Create a dependency element /*{{{*/
850 // ---------------------------------------------------------------------
851 /* This creates a dependency element in the tree. It is linked to the
852 version and to the package that it is pointing to. */
853 bool pkgCacheGenerator::NewDepends(pkgCache::PkgIterator &Pkg,
854 pkgCache::VerIterator &Ver,
855 map_pointer_t const Version,
856 uint8_t const Op,
857 uint8_t const Type,
858 map_pointer_t* &OldDepLast)
859 {
860 void const * const oldMap = Map.Data();
861 // Get a structure
862 map_pointer_t const Dependency = AllocateInMap(sizeof(pkgCache::Dependency));
863 if (unlikely(Dependency == 0))
864 return false;
865
866 bool isDuplicate = false;
867 map_pointer_t DependencyData = 0;
868 map_pointer_t PreviousData = 0;
869 if (Pkg->RevDepends != 0)
870 {
871 pkgCache::Dependency const * const L = Cache.DepP + Pkg->RevDepends;
872 DependencyData = L->DependencyData;
873 do {
874 pkgCache::DependencyData const * const D = Cache.DepDataP + DependencyData;
875 if (Version > D->Version)
876 break;
877 if (D->Version == Version && D->Type == Type && D->CompareOp == Op)
878 {
879 isDuplicate = true;
880 break;
881 }
882 PreviousData = DependencyData;
883 DependencyData = D->NextData;
884 } while (DependencyData != 0);
885 }
886
887 if (isDuplicate == false)
888 {
889 DependencyData = AllocateInMap(sizeof(pkgCache::DependencyData));
890 if (unlikely(DependencyData == 0))
891 return false;
892 }
893
894 pkgCache::Dependency * Link = Cache.DepP + Dependency;
895 Link->ParentVer = Ver.Index();
896 Link->DependencyData = DependencyData;
897 Link->ID = Cache.HeaderP->DependsCount++;
898
899 pkgCache::DepIterator Dep(Cache, Link);
900 if (isDuplicate == false)
901 {
902 Dep->Type = Type;
903 Dep->CompareOp = Op;
904 Dep->Version = Version;
905 Dep->Package = Pkg.Index();
906 ++Cache.HeaderP->DependsDataCount;
907 if (PreviousData != 0)
908 {
909 pkgCache::DependencyData * const D = Cache.DepDataP + PreviousData;
910 Dep->NextData = D->NextData;
911 D->NextData = DependencyData;
912 }
913 else if (Pkg->RevDepends != 0)
914 {
915 pkgCache::Dependency const * const D = Cache.DepP + Pkg->RevDepends;
916 Dep->NextData = D->DependencyData;
917 }
918 }
919
920 if (isDuplicate == true || PreviousData != 0)
921 {
922 pkgCache::Dependency * const L = Cache.DepP + Pkg->RevDepends;
923 Link->NextRevDepends = L->NextRevDepends;
924 L->NextRevDepends = Dependency;
925 }
926 else
927 {
928 Link->NextRevDepends = Pkg->RevDepends;
929 Pkg->RevDepends = Dependency;
930 }
931
932
933 // Do we know where to link the Dependency to?
934 if (OldDepLast == NULL)
935 {
936 OldDepLast = &Ver->DependsList;
937 for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; ++D)
938 OldDepLast = &D->NextDepends;
939 } else if (oldMap != Map.Data())
940 OldDepLast += (map_pointer_t const * const) Map.Data() - (map_pointer_t const * const) oldMap;
941
942 Dep->NextDepends = *OldDepLast;
943 *OldDepLast = Dependency;
944 OldDepLast = &Dep->NextDepends;
945 return true;
946 }
947 /*}}}*/
948 // ListParser::NewDepends - Create the environment for a new dependency /*{{{*/
949 // ---------------------------------------------------------------------
950 /* This creates a Group and the Package to link this dependency to if
951 needed and handles also the caching of the old endpoint */
952 bool pkgCacheListParser::NewDepends(pkgCache::VerIterator &Ver,
953 const string &PackageName,
954 const string &Arch,
955 const string &Version,
956 uint8_t const Op,
957 uint8_t const Type)
958 {
959 pkgCache::GrpIterator Grp;
960 Dynamic<pkgCache::GrpIterator> DynGrp(Grp);
961 if (unlikely(Owner->NewGroup(Grp, PackageName) == false))
962 return false;
963
964 map_stringitem_t idxVersion = 0;
965 if (Version.empty() == false)
966 {
967 int const CmpOp = Op & 0x0F;
968 // =-deps are used (79:1) for lockstep on same-source packages (e.g. data-packages)
969 if (CmpOp == pkgCache::Dep::Equals && strcmp(Version.c_str(), Ver.VerStr()) == 0)
970 idxVersion = Ver->VerStr;
971
972 if (idxVersion == 0)
973 {
974 idxVersion = StoreString(pkgCacheGenerator::VERSIONNUMBER, Version);
975 if (unlikely(idxVersion == 0))
976 return false;
977 }
978 }
979
980 bool const isNegative = (Type == pkgCache::Dep::DpkgBreaks ||
981 Type == pkgCache::Dep::Conflicts ||
982 Type == pkgCache::Dep::Replaces);
983
984 pkgCache::PkgIterator Pkg;
985 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
986 if (isNegative == false || (Op & pkgCache::Dep::ArchSpecific) == pkgCache::Dep::ArchSpecific || Grp->FirstPackage == 0)
987 {
988 // Locate the target package
989 Pkg = Grp.FindPkg(Arch);
990 if (Pkg.end() == true) {
991 if (unlikely(Owner->NewPackage(Pkg, PackageName, Arch) == false))
992 return false;
993 }
994
995 /* Caching the old end point speeds up generation substantially */
996 if (OldDepVer != Ver) {
997 OldDepLast = NULL;
998 OldDepVer = Ver;
999 }
1000
1001 return Owner->NewDepends(Pkg, Ver, idxVersion, Op, Type, OldDepLast);
1002 }
1003 else
1004 {
1005 /* Caching the old end point speeds up generation substantially */
1006 if (OldDepVer != Ver) {
1007 OldDepLast = NULL;
1008 OldDepVer = Ver;
1009 }
1010
1011 for (Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
1012 {
1013 if (Owner->NewDepends(Pkg, Ver, idxVersion, Op, Type, OldDepLast) == false)
1014 return false;
1015 }
1016 }
1017 return true;
1018 }
1019 /*}}}*/
1020 // ListParser::NewProvides - Create a Provides element /*{{{*/
1021 bool pkgCacheListParser::NewProvides(pkgCache::VerIterator &Ver,
1022 const string &PkgName,
1023 const string &PkgArch,
1024 const string &Version,
1025 uint8_t const Flags)
1026 {
1027 pkgCache const &Cache = Owner->Cache;
1028
1029 // We do not add self referencing provides
1030 if (Ver.ParentPkg().Name() == PkgName && (PkgArch == Ver.ParentPkg().Arch() ||
1031 (PkgArch == "all" && strcmp((Cache.StrP + Cache.HeaderP->Architecture), Ver.ParentPkg().Arch()) == 0)))
1032 return true;
1033
1034 // Locate the target package
1035 pkgCache::PkgIterator Pkg;
1036 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
1037 if (unlikely(Owner->NewPackage(Pkg,PkgName, PkgArch) == false))
1038 return false;
1039
1040 map_stringitem_t idxProvideVersion = 0;
1041 if (Version.empty() == false) {
1042 idxProvideVersion = StoreString(pkgCacheGenerator::VERSIONNUMBER, Version);
1043 if (unlikely(idxProvideVersion == 0))
1044 return false;
1045 }
1046 return Owner->NewProvides(Ver, Pkg, idxProvideVersion, Flags);
1047 }
1048 bool pkgCacheGenerator::NewProvides(pkgCache::VerIterator &Ver,
1049 pkgCache::PkgIterator &Pkg,
1050 map_pointer_t const ProvideVersion,
1051 uint8_t const Flags)
1052 {
1053 // Get a structure
1054 map_pointer_t const Provides = AllocateInMap(sizeof(pkgCache::Provides));
1055 if (unlikely(Provides == 0))
1056 return false;
1057 ++Cache.HeaderP->ProvidesCount;
1058
1059 // Fill it in
1060 pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
1061 Prv->Version = Ver.Index();
1062 Prv->ProvideVersion = ProvideVersion;
1063 Prv->Flags = Flags;
1064 Prv->NextPkgProv = Ver->ProvidesList;
1065 Ver->ProvidesList = Prv.Index();
1066
1067 // Link it to the package
1068 Prv->ParentPkg = Pkg.Index();
1069 Prv->NextProvides = Pkg->ProvidesList;
1070 Pkg->ProvidesList = Prv.Index();
1071 return true;
1072 }
1073 /*}}}*/
1074 // ListParser::NewProvidesAllArch - add provides for all architectures /*{{{*/
1075 bool pkgCacheListParser::NewProvidesAllArch(pkgCache::VerIterator &Ver, string const &Package,
1076 string const &Version, uint8_t const Flags) {
1077 pkgCache &Cache = Owner->Cache;
1078 pkgCache::GrpIterator const Grp = Cache.FindGrp(Package);
1079 if (Grp.end() == true)
1080 return NewProvides(Ver, Package, Cache.NativeArch(), Version, Flags);
1081 else
1082 {
1083 map_stringitem_t idxProvideVersion = 0;
1084 if (Version.empty() == false) {
1085 idxProvideVersion = StoreString(pkgCacheGenerator::VERSIONNUMBER, Version);
1086 if (unlikely(idxProvideVersion == 0))
1087 return false;
1088 }
1089
1090 bool const isImplicit = (Flags & pkgCache::Flag::MultiArchImplicit) == pkgCache::Flag::MultiArchImplicit;
1091 bool const isArchSpecific = (Flags & pkgCache::Flag::ArchSpecific) == pkgCache::Flag::ArchSpecific;
1092 pkgCache::PkgIterator const OwnerPkg = Ver.ParentPkg();
1093 for (pkgCache::PkgIterator Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
1094 {
1095 if (isImplicit && OwnerPkg == Pkg)
1096 continue;
1097 if (isArchSpecific == false && APT::Configuration::checkArchitecture(OwnerPkg.Arch()) == false)
1098 continue;
1099 if (Owner->NewProvides(Ver, Pkg, idxProvideVersion, Flags) == false)
1100 return false;
1101 }
1102 }
1103 return true;
1104 }
1105 /*}}}*/
1106 bool pkgCacheListParser::SameVersion(unsigned short const Hash, /*{{{*/
1107 pkgCache::VerIterator const &Ver)
1108 {
1109 return Hash == Ver->Hash;
1110 }
1111 /*}}}*/
1112 // CacheGenerator::SelectReleaseFile - Select the current release file the indexes belong to /*{{{*/
1113 bool pkgCacheGenerator::SelectReleaseFile(const string &File,const string &Site,
1114 unsigned long Flags)
1115 {
1116 if (File.empty() && Site.empty())
1117 {
1118 CurrentRlsFile = NULL;
1119 return true;
1120 }
1121
1122 // Get some space for the structure
1123 map_pointer_t const idxFile = AllocateInMap(sizeof(*CurrentRlsFile));
1124 if (unlikely(idxFile == 0))
1125 return false;
1126 CurrentRlsFile = Cache.RlsFileP + idxFile;
1127
1128 // Fill it in
1129 map_stringitem_t const idxFileName = WriteStringInMap(File);
1130 map_stringitem_t const idxSite = StoreString(MIXED, Site);
1131 if (unlikely(idxFileName == 0 || idxSite == 0))
1132 return false;
1133 CurrentRlsFile->FileName = idxFileName;
1134 CurrentRlsFile->Site = idxSite;
1135 CurrentRlsFile->NextFile = Cache.HeaderP->RlsFileList;
1136 CurrentRlsFile->Flags = Flags;
1137 CurrentRlsFile->ID = Cache.HeaderP->ReleaseFileCount;
1138 RlsFileName = File;
1139 Cache.HeaderP->RlsFileList = CurrentRlsFile - Cache.RlsFileP;
1140 Cache.HeaderP->ReleaseFileCount++;
1141
1142 return true;
1143 }
1144 /*}}}*/
1145 // CacheGenerator::SelectFile - Select the current file being parsed /*{{{*/
1146 // ---------------------------------------------------------------------
1147 /* This is used to select which file is to be associated with all newly
1148 added versions. The caller is responsible for setting the IMS fields. */
1149 bool pkgCacheGenerator::SelectFile(std::string const &File,
1150 pkgIndexFile const &Index,
1151 std::string const &Architecture,
1152 std::string const &Component,
1153 unsigned long const Flags)
1154 {
1155 // Get some space for the structure
1156 map_pointer_t const idxFile = AllocateInMap(sizeof(*CurrentFile));
1157 if (unlikely(idxFile == 0))
1158 return false;
1159 CurrentFile = Cache.PkgFileP + idxFile;
1160
1161 // Fill it in
1162 map_stringitem_t const idxFileName = WriteStringInMap(File);
1163 if (unlikely(idxFileName == 0))
1164 return false;
1165 CurrentFile->FileName = idxFileName;
1166 CurrentFile->NextFile = Cache.HeaderP->FileList;
1167 CurrentFile->ID = Cache.HeaderP->PackageFileCount;
1168 map_stringitem_t const idxIndexType = StoreString(MIXED, Index.GetType()->Label);
1169 if (unlikely(idxIndexType == 0))
1170 return false;
1171 CurrentFile->IndexType = idxIndexType;
1172 if (Architecture.empty())
1173 CurrentFile->Architecture = 0;
1174 else
1175 {
1176 map_stringitem_t const arch = StoreString(pkgCacheGenerator::MIXED, Architecture);
1177 if (unlikely(arch == 0))
1178 return false;
1179 CurrentFile->Architecture = arch;
1180 }
1181 map_stringitem_t const component = StoreString(pkgCacheGenerator::MIXED, Component);
1182 if (unlikely(component == 0))
1183 return false;
1184 CurrentFile->Component = component;
1185 CurrentFile->Flags = Flags;
1186 if (CurrentRlsFile != NULL)
1187 CurrentFile->Release = CurrentRlsFile - Cache.RlsFileP;
1188 else
1189 CurrentFile->Release = 0;
1190 PkgFileName = File;
1191 Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
1192 Cache.HeaderP->PackageFileCount++;
1193
1194 if (Progress != 0)
1195 Progress->SubProgress(Index.Size());
1196 return true;
1197 }
1198 /*}}}*/
1199 // CacheGenerator::WriteUniqueString - Insert a unique string /*{{{*/
1200 // ---------------------------------------------------------------------
1201 /* This is used to create handles to strings. Given the same text it
1202 always returns the same number */
1203 map_stringitem_t pkgCacheGenerator::StoreString(enum StringType const type, const char *S,
1204 unsigned int Size)
1205 {
1206 std::string const key(S, Size);
1207
1208 std::map<std::string,map_stringitem_t> * strings;
1209 switch(type) {
1210 case MIXED: strings = &strMixed; break;
1211 case PKGNAME: strings = &strPkgNames; break;
1212 case VERSIONNUMBER: strings = &strVersions; break;
1213 case SECTION: strings = &strSections; break;
1214 default: _error->Fatal("Unknown enum type used for string storage of '%s'", key.c_str()); return 0;
1215 }
1216
1217 std::map<std::string,map_stringitem_t>::const_iterator const item = strings->find(key);
1218 if (item != strings->end())
1219 return item->second;
1220
1221 map_stringitem_t const idxString = WriteStringInMap(S,Size);
1222 strings->insert(std::make_pair(key, idxString));
1223 return idxString;
1224 }
1225 /*}}}*/
1226 // CheckValidity - Check that a cache is up-to-date /*{{{*/
1227 // ---------------------------------------------------------------------
1228 /* This just verifies that each file in the list of index files exists,
1229 has matching attributes with the cache and the cache does not have
1230 any extra files. */
1231 static bool CheckValidity(const string &CacheFile,
1232 pkgSourceList &List,
1233 FileIterator const Start,
1234 FileIterator const End,
1235 MMap **OutMap = 0)
1236 {
1237 bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
1238 // No file, certainly invalid
1239 if (CacheFile.empty() == true || FileExists(CacheFile) == false)
1240 {
1241 if (Debug == true)
1242 std::clog << "CacheFile " << CacheFile << " doesn't exist" << std::endl;
1243 return false;
1244 }
1245
1246 if (List.GetLastModifiedTime() > GetModificationTime(CacheFile))
1247 {
1248 if (Debug == true)
1249 std::clog << "sources.list is newer than the cache" << std::endl;
1250 return false;
1251 }
1252
1253 // Map it
1254 FileFd CacheF(CacheFile,FileFd::ReadOnly);
1255 std::unique_ptr<MMap> Map(new MMap(CacheF,0));
1256 pkgCache Cache(Map.get());
1257 if (_error->PendingError() == true || Map->Size() == 0)
1258 {
1259 if (Debug == true)
1260 std::clog << "Errors are pending or Map is empty() for " << CacheFile << std::endl;
1261 _error->Discard();
1262 return false;
1263 }
1264
1265 std::unique_ptr<bool[]> RlsVisited(new bool[Cache.HeaderP->ReleaseFileCount]);
1266 memset(RlsVisited.get(),0,sizeof(RlsVisited[0])*Cache.HeaderP->ReleaseFileCount);
1267 std::vector<pkgIndexFile *> Files;
1268 for (pkgSourceList::const_iterator i = List.begin(); i != List.end(); ++i)
1269 {
1270 if (Debug == true)
1271 std::clog << "Checking RlsFile " << (*i)->Describe() << ": ";
1272 pkgCache::RlsFileIterator const RlsFile = (*i)->FindInCache(Cache, true);
1273 if (RlsFile.end() == true)
1274 {
1275 if (Debug == true)
1276 std::clog << "FindInCache returned end-Pointer" << std::endl;
1277 return false;
1278 }
1279
1280 RlsVisited[RlsFile->ID] = true;
1281 if (Debug == true)
1282 std::clog << "with ID " << RlsFile->ID << " is valid" << std::endl;
1283
1284 std::vector <pkgIndexFile *> const * const Indexes = (*i)->GetIndexFiles();
1285 std::copy_if(Indexes->begin(), Indexes->end(), std::back_inserter(Files),
1286 [](pkgIndexFile const * const I) { return I->HasPackages(); });
1287 }
1288 for (unsigned I = 0; I != Cache.HeaderP->ReleaseFileCount; ++I)
1289 if (RlsVisited[I] == false)
1290 {
1291 if (Debug == true)
1292 std::clog << "RlsFile with ID" << I << " wasn't visited" << std::endl;
1293 return false;
1294 }
1295
1296 std::copy(Start, End, std::back_inserter(Files));
1297
1298 /* Now we check every index file, see if it is in the cache,
1299 verify the IMS data and check that it is on the disk too.. */
1300 std::unique_ptr<bool[]> Visited(new bool[Cache.HeaderP->PackageFileCount]);
1301 memset(Visited.get(),0,sizeof(Visited[0])*Cache.HeaderP->PackageFileCount);
1302 for (std::vector<pkgIndexFile *>::const_reverse_iterator PkgFile = Files.rbegin(); PkgFile != Files.rend(); ++PkgFile)
1303 {
1304 if (Debug == true)
1305 std::clog << "Checking PkgFile " << (*PkgFile)->Describe() << ": ";
1306 if ((*PkgFile)->Exists() == false)
1307 {
1308 if (Debug == true)
1309 std::clog << "file doesn't exist" << std::endl;
1310 continue;
1311 }
1312
1313 // FindInCache is also expected to do an IMS check.
1314 pkgCache::PkgFileIterator File = (*PkgFile)->FindInCache(Cache);
1315 if (File.end() == true)
1316 {
1317 if (Debug == true)
1318 std::clog << "FindInCache returned end-Pointer" << std::endl;
1319 return false;
1320 }
1321
1322 Visited[File->ID] = true;
1323 if (Debug == true)
1324 std::clog << "with ID " << File->ID << " is valid" << std::endl;
1325 }
1326
1327 for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++)
1328 if (Visited[I] == false)
1329 {
1330 if (Debug == true)
1331 std::clog << "PkgFile with ID" << I << " wasn't visited" << std::endl;
1332 return false;
1333 }
1334
1335 if (_error->PendingError() == true)
1336 {
1337 if (Debug == true)
1338 {
1339 std::clog << "Validity failed because of pending errors:" << std::endl;
1340 _error->DumpErrors();
1341 }
1342 _error->Discard();
1343 return false;
1344 }
1345
1346 if (OutMap != 0)
1347 *OutMap = Map.release();
1348 return true;
1349 }
1350 /*}}}*/
1351 // ComputeSize - Compute the total size of a bunch of files /*{{{*/
1352 // ---------------------------------------------------------------------
1353 /* Size is kind of an abstract notion that is only used for the progress
1354 meter */
1355 static map_filesize_t ComputeSize(pkgSourceList const * const List, FileIterator Start,FileIterator End)
1356 {
1357 map_filesize_t TotalSize = 0;
1358 if (List != NULL)
1359 {
1360 for (pkgSourceList::const_iterator i = List->begin(); i != List->end(); ++i)
1361 {
1362 std::vector <pkgIndexFile *> *Indexes = (*i)->GetIndexFiles();
1363 for (std::vector<pkgIndexFile *>::const_iterator j = Indexes->begin(); j != Indexes->end(); ++j)
1364 if ((*j)->HasPackages() == true)
1365 TotalSize += (*j)->Size();
1366 }
1367 }
1368
1369 for (; Start < End; ++Start)
1370 {
1371 if ((*Start)->HasPackages() == false)
1372 continue;
1373 TotalSize += (*Start)->Size();
1374 }
1375 return TotalSize;
1376 }
1377 /*}}}*/
1378 // BuildCache - Merge the list of index files into the cache /*{{{*/
1379 static bool BuildCache(pkgCacheGenerator &Gen,
1380 OpProgress * const Progress,
1381 map_filesize_t &CurrentSize,map_filesize_t TotalSize,
1382 pkgSourceList const * const List,
1383 FileIterator const Start, FileIterator const End)
1384 {
1385 std::vector<pkgIndexFile *> Files;
1386 bool mergeFailure = false;
1387
1388 auto const indexFileMerge = [&](pkgIndexFile * const I) {
1389 if (I->HasPackages() == false || mergeFailure)
1390 return;
1391
1392 if (I->Exists() == false)
1393 return;
1394
1395 if (I->FindInCache(Gen.GetCache()).end() == false)
1396 {
1397 _error->Warning("Duplicate sources.list entry %s",
1398 I->Describe().c_str());
1399 return;
1400 }
1401
1402 map_filesize_t const Size = I->Size();
1403 if (Progress != NULL)
1404 Progress->OverallProgress(CurrentSize, TotalSize, Size, _("Reading package lists"));
1405 CurrentSize += Size;
1406
1407 if (I->Merge(Gen,Progress) == false)
1408 mergeFailure = true;
1409 };
1410
1411 if (List != NULL)
1412 {
1413 for (pkgSourceList::const_iterator i = List->begin(); i != List->end(); ++i)
1414 {
1415 if ((*i)->FindInCache(Gen.GetCache(), false).end() == false)
1416 {
1417 _error->Warning("Duplicate sources.list entry %s",
1418 (*i)->Describe().c_str());
1419 continue;
1420 }
1421
1422 if ((*i)->Merge(Gen, Progress) == false)
1423 return false;
1424
1425 std::vector <pkgIndexFile *> *Indexes = (*i)->GetIndexFiles();
1426 if (Indexes != NULL)
1427 std::for_each(Indexes->begin(), Indexes->end(), indexFileMerge);
1428 if (mergeFailure)
1429 return false;
1430 }
1431 }
1432
1433 if (Start != End)
1434 {
1435 Gen.SelectReleaseFile("", "");
1436 std::for_each(Start, End, indexFileMerge);
1437 if (mergeFailure)
1438 return false;
1439 }
1440 return true;
1441 }
1442 /*}}}*/
1443 // CacheGenerator::MakeStatusCache - Construct the status cache /*{{{*/
1444 // ---------------------------------------------------------------------
1445 /* This makes sure that the status cache (the cache that has all
1446 index files from the sources list and all local ones) is ready
1447 to be mmaped. If OutMap is not zero then a MMap object representing
1448 the cache will be stored there. This is pretty much mandetory if you
1449 are using AllowMem. AllowMem lets the function be run as non-root
1450 where it builds the cache 'fast' into a memory buffer. */
1451 static DynamicMMap* CreateDynamicMMap(FileFd * const CacheF, unsigned long Flags)
1452 {
1453 map_filesize_t const MapStart = _config->FindI("APT::Cache-Start", 24*1024*1024);
1454 map_filesize_t const MapGrow = _config->FindI("APT::Cache-Grow", 1*1024*1024);
1455 map_filesize_t const MapLimit = _config->FindI("APT::Cache-Limit", 0);
1456 Flags |= MMap::Moveable;
1457 if (_config->FindB("APT::Cache-Fallback", false) == true)
1458 Flags |= MMap::Fallback;
1459 if (CacheF != NULL)
1460 return new DynamicMMap(*CacheF, Flags, MapStart, MapGrow, MapLimit);
1461 else
1462 return new DynamicMMap(Flags, MapStart, MapGrow, MapLimit);
1463 }
1464 static bool writeBackMMapToFile(pkgCacheGenerator * const Gen, DynamicMMap * const Map,
1465 std::string const &FileName)
1466 {
1467 FileFd SCacheF(FileName, FileFd::WriteAtomic);
1468 if (_error->PendingError() == true)
1469 return false;
1470
1471 fchmod(SCacheF.Fd(),0644);
1472
1473 // Write out the main data
1474 if (SCacheF.Write(Map->Data(),Map->Size()) == false)
1475 return _error->Error(_("IO Error saving source cache"));
1476 SCacheF.Sync();
1477
1478 // Write out the proper header
1479 Gen->GetCache().HeaderP->Dirty = false;
1480 if (SCacheF.Seek(0) == false ||
1481 SCacheF.Write(Map->Data(),sizeof(*Gen->GetCache().HeaderP)) == false)
1482 return _error->Error(_("IO Error saving source cache"));
1483 Gen->GetCache().HeaderP->Dirty = true;
1484 SCacheF.Sync();
1485 return true;
1486 }
1487 static bool loadBackMMapFromFile(std::unique_ptr<pkgCacheGenerator> &Gen,
1488 std::unique_ptr<DynamicMMap> &Map, OpProgress * const Progress, std::string const &FileName)
1489 {
1490 Map.reset(CreateDynamicMMap(NULL, 0));
1491 FileFd CacheF(FileName, FileFd::ReadOnly);
1492 map_pointer_t const alloc = Map->RawAllocate(CacheF.Size());
1493 if ((alloc == 0 && _error->PendingError())
1494 || CacheF.Read((unsigned char *)Map->Data() + alloc,
1495 CacheF.Size()) == false)
1496 return false;
1497 Gen.reset(new pkgCacheGenerator(Map.get(),Progress));
1498 return true;
1499 }
1500 APT_DEPRECATED bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress,
1501 MMap **OutMap, bool AllowMem)
1502 { return pkgCacheGenerator::MakeStatusCache(List, &Progress, OutMap, AllowMem); }
1503 bool pkgCacheGenerator::MakeStatusCache(pkgSourceList &List,OpProgress *Progress,
1504 MMap **OutMap,bool AllowMem)
1505 {
1506 bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
1507
1508 std::vector<pkgIndexFile *> Files;
1509 if (_system->AddStatusFiles(Files) == false)
1510 return false;
1511
1512 // Decide if we can write to the files..
1513 string const CacheFile = _config->FindFile("Dir::Cache::pkgcache");
1514 string const SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
1515
1516 // ensure the cache directory exists
1517 if (CacheFile.empty() == false || SrcCacheFile.empty() == false)
1518 {
1519 string dir = _config->FindDir("Dir::Cache");
1520 size_t const len = dir.size();
1521 if (len > 5 && dir.find("/apt/", len - 6, 5) == len - 5)
1522 dir = dir.substr(0, len - 5);
1523 if (CacheFile.empty() == false)
1524 CreateDirectory(dir, flNotFile(CacheFile));
1525 if (SrcCacheFile.empty() == false)
1526 CreateDirectory(dir, flNotFile(SrcCacheFile));
1527 }
1528
1529 if (Progress != NULL)
1530 Progress->OverallProgress(0,1,1,_("Reading package lists"));
1531
1532 bool pkgcache_fine = false;
1533 bool srcpkgcache_fine = false;
1534 bool volatile_fine = List.GetVolatileFiles().empty();
1535
1536 if (CheckValidity(CacheFile, List, Files.begin(), Files.end(), volatile_fine ? OutMap : NULL) == true)
1537 {
1538 if (Debug == true)
1539 std::clog << "pkgcache.bin is valid - no need to build any cache" << std::endl;
1540 pkgcache_fine = true;
1541 srcpkgcache_fine = true;
1542 }
1543 if (pkgcache_fine == false)
1544 {
1545 if (CheckValidity(SrcCacheFile, List, Files.end(), Files.end()) == true)
1546 {
1547 if (Debug == true)
1548 std::clog << "srcpkgcache.bin is valid - it can be reused" << std::endl;
1549 srcpkgcache_fine = true;
1550 }
1551 }
1552
1553 if (volatile_fine == true && srcpkgcache_fine == true && pkgcache_fine == true)
1554 {
1555 if (Progress != NULL)
1556 Progress->OverallProgress(1,1,1,_("Reading package lists"));
1557 return true;
1558 }
1559
1560 bool Writeable = false;
1561 if (srcpkgcache_fine == false || pkgcache_fine == false)
1562 {
1563 if (CacheFile.empty() == false)
1564 Writeable = access(flNotFile(CacheFile).c_str(),W_OK) == 0;
1565 else if (SrcCacheFile.empty() == false)
1566 Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0;
1567
1568 if (Debug == true)
1569 std::clog << "Do we have write-access to the cache files? " << (Writeable ? "YES" : "NO") << std::endl;
1570
1571 if (Writeable == false && AllowMem == false)
1572 {
1573 if (CacheFile.empty() == false)
1574 return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str());
1575 else if (SrcCacheFile.empty() == false)
1576 return _error->Error(_("Unable to write to %s"),flNotFile(SrcCacheFile).c_str());
1577 else
1578 return _error->Error("Unable to create caches as file usage is disabled, but memory not allowed either!");
1579 }
1580 }
1581
1582 // At this point we know we need to construct something, so get storage ready
1583 std::unique_ptr<DynamicMMap> Map(CreateDynamicMMap(NULL, 0));
1584 if (Debug == true)
1585 std::clog << "Open memory Map (not filebased)" << std::endl;
1586
1587 std::unique_ptr<pkgCacheGenerator> Gen{nullptr};
1588 map_filesize_t CurrentSize = 0;
1589 std::vector<pkgIndexFile*> VolatileFiles = List.GetVolatileFiles();
1590 map_filesize_t TotalSize = ComputeSize(NULL, VolatileFiles.begin(), VolatileFiles.end());
1591 if (srcpkgcache_fine == true && pkgcache_fine == false)
1592 {
1593 if (Debug == true)
1594 std::clog << "srcpkgcache.bin was valid - populate MMap with it" << std::endl;
1595 if (loadBackMMapFromFile(Gen, Map, Progress, SrcCacheFile) == false)
1596 return false;
1597 srcpkgcache_fine = true;
1598 TotalSize += ComputeSize(NULL, Files.begin(), Files.end());
1599 }
1600 else if (srcpkgcache_fine == false)
1601 {
1602 if (Debug == true)
1603 std::clog << "srcpkgcache.bin is NOT valid - rebuild" << std::endl;
1604 Gen.reset(new pkgCacheGenerator(Map.get(),Progress));
1605
1606 TotalSize += ComputeSize(&List, Files.begin(),Files.end());
1607 if (BuildCache(*Gen, Progress, CurrentSize, TotalSize, &List,
1608 Files.end(),Files.end()) == false)
1609 return false;
1610
1611 if (Writeable == true && SrcCacheFile.empty() == false)
1612 if (writeBackMMapToFile(Gen.get(), Map.get(), SrcCacheFile) == false)
1613 return false;
1614 }
1615
1616 if (pkgcache_fine == false)
1617 {
1618 if (Debug == true)
1619 std::clog << "Building status cache in pkgcache.bin now" << std::endl;
1620 if (BuildCache(*Gen, Progress, CurrentSize, TotalSize, NULL,
1621 Files.begin(), Files.end()) == false)
1622 return false;
1623
1624 if (Writeable == true && CacheFile.empty() == false)
1625 if (writeBackMMapToFile(Gen.get(), Map.get(), CacheFile) == false)
1626 return false;
1627 }
1628
1629 if (Debug == true)
1630 std::clog << "Caches done. Now bring in the volatile files (if any)" << std::endl;
1631
1632 if (volatile_fine == false)
1633 {
1634 if (Gen == nullptr)
1635 {
1636 if (Debug == true)
1637 std::clog << "Populate new MMap with cachefile contents" << std::endl;
1638 if (loadBackMMapFromFile(Gen, Map, Progress, CacheFile) == false)
1639 return false;
1640 }
1641
1642 Files = List.GetVolatileFiles();
1643 if (BuildCache(*Gen, Progress, CurrentSize, TotalSize, NULL,
1644 Files.begin(), Files.end()) == false)
1645 return false;
1646 }
1647
1648 if (OutMap != nullptr)
1649 *OutMap = Map.release();
1650
1651 if (Debug == true)
1652 std::clog << "Everything is ready for shipping" << std::endl;
1653 return true;
1654 }
1655 /*}}}*/
1656 // CacheGenerator::MakeOnlyStatusCache - Build only a status files cache/*{{{*/
1657 // ---------------------------------------------------------------------
1658 /* */
1659 APT_DEPRECATED bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap)
1660 { return pkgCacheGenerator::MakeOnlyStatusCache(&Progress, OutMap); }
1661 bool pkgCacheGenerator::MakeOnlyStatusCache(OpProgress *Progress,DynamicMMap **OutMap)
1662 {
1663 std::vector<pkgIndexFile *> Files;
1664 if (_system->AddStatusFiles(Files) == false)
1665 return false;
1666
1667 std::unique_ptr<DynamicMMap> Map(CreateDynamicMMap(NULL, 0));
1668 map_filesize_t CurrentSize = 0;
1669 map_filesize_t TotalSize = 0;
1670
1671 TotalSize = ComputeSize(NULL, Files.begin(), Files.end());
1672
1673 // Build the status cache
1674 if (Progress != NULL)
1675 Progress->OverallProgress(0,1,1,_("Reading package lists"));
1676 pkgCacheGenerator Gen(Map.get(),Progress);
1677 if (_error->PendingError() == true)
1678 return false;
1679 if (BuildCache(Gen,Progress,CurrentSize,TotalSize, NULL,
1680 Files.begin(), Files.end()) == false)
1681 return false;
1682
1683 if (_error->PendingError() == true)
1684 return false;
1685 *OutMap = Map.release();
1686
1687 return true;
1688 }
1689 /*}}}*/
1690 // IsDuplicateDescription /*{{{*/
1691 static bool IsDuplicateDescription(pkgCache::DescIterator Desc,
1692 MD5SumValue const &CurMd5, std::string const &CurLang)
1693 {
1694 // Descriptions in the same link-list have all the same md5
1695 if (Desc.end() == true || MD5SumValue(Desc.md5()) != CurMd5)
1696 return false;
1697 for (; Desc.end() == false; ++Desc)
1698 if (Desc.LanguageCode() == CurLang)
1699 return true;
1700 return false;
1701 }
1702 /*}}}*/
1703
1704 pkgCacheListParser::pkgCacheListParser() : Owner(NULL), OldDepLast(NULL), d(NULL) {}
1705 pkgCacheListParser::~pkgCacheListParser() {}