]> git.saurik.com Git - apt.git/blob - apt-pkg/pkgcachegen.cc
Consider md5sum no longer a usable hash
[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 pkgCache::PrvIterator Prv;
577 Dynamic<pkgCache::PrvIterator> DynPrv(Prv);
578 for (Prv = M.ProvidesList(); Prv.end() == false; ++Prv)
579 {
580 if ((Prv->Flags & pkgCache::Flag::ArchSpecific) != 0)
581 continue;
582 pkgCache::VerIterator Ver = Prv.OwnerVer();
583 Dynamic<pkgCache::VerIterator> DynVer(Ver);
584 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed ||
585 ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign &&
586 (Prv->Flags & pkgCache::Flag::MultiArchImplicit) == 0))
587 if (NewProvides(Ver, Pkg, Prv->ProvideVersion, Prv->Flags) == false)
588 return false;
589 }
590 }
591
592
593 pkgCache::PkgIterator P;
594 pkgCache::VerIterator Ver;
595 Dynamic<pkgCache::PkgIterator> DynP(P);
596 Dynamic<pkgCache::VerIterator> DynVer(Ver);
597
598 for (P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
599 for (Ver = P.VersionList(); Ver.end() == false; ++Ver)
600 if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
601 if (NewProvides(Ver, Pkg, Ver->VerStr, pkgCache::Flag::MultiArchImplicit) == false)
602 return false;
603 }
604 // and negative dependencies, don't forget negative dependencies
605 {
606 pkgCache::PkgIterator const M = Grp.FindPreferredPkg(false);
607 if (M.end() == false) {
608 pkgCache::DepIterator Dep;
609 Dynamic<pkgCache::DepIterator> DynDep(Dep);
610 for (Dep = M.RevDependsList(); Dep.end() == false; ++Dep)
611 {
612 if ((Dep->CompareOp & (pkgCache::Dep::ArchSpecific | pkgCache::Dep::MultiArchImplicit)) != 0)
613 continue;
614 if (Dep->Type != pkgCache::Dep::DpkgBreaks && Dep->Type != pkgCache::Dep::Conflicts &&
615 Dep->Type != pkgCache::Dep::Replaces)
616 continue;
617 pkgCache::VerIterator Ver = Dep.ParentVer();
618 Dynamic<pkgCache::VerIterator> DynVer(Ver);
619 map_pointer_t * unused = NULL;
620 if (NewDepends(Pkg, Ver, Dep->Version, Dep->CompareOp, Dep->Type, unused) == false)
621 return false;
622 }
623 }
624 }
625
626 // this package is the new last package
627 pkgCache::PkgIterator LastPkg(Cache, Cache.PkgP + Grp->LastPackage);
628 Pkg->NextPackage = LastPkg->NextPackage;
629 LastPkg->NextPackage = Package;
630 }
631 Grp->LastPackage = Package;
632 return true;
633 }
634 /*}}}*/
635 // CacheGenerator::AddImplicitDepends /*{{{*/
636 bool pkgCacheGenerator::AddImplicitDepends(pkgCache::GrpIterator &G,
637 pkgCache::PkgIterator &P,
638 pkgCache::VerIterator &V)
639 {
640 // copy P.Arch() into a string here as a cache remap
641 // in NewDepends() later may alter the pointer location
642 string Arch = P.Arch() == NULL ? "" : P.Arch();
643 map_pointer_t *OldDepLast = NULL;
644 /* MultiArch handling introduces a lot of implicit Dependencies:
645 - MultiArch: same → Co-Installable if they have the same version
646 - All others conflict with all other group members */
647 bool const coInstall = ((V->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same);
648 pkgCache::PkgIterator D = G.PackageList();
649 Dynamic<pkgCache::PkgIterator> DynD(D);
650 map_stringitem_t const VerStrIdx = V->VerStr;
651 for (; D.end() != true; D = G.NextPkg(D))
652 {
653 if (Arch == D.Arch() || D->VersionList == 0)
654 continue;
655 /* We allow only one installed arch at the time
656 per group, therefore each group member conflicts
657 with all other group members */
658 if (coInstall == true)
659 {
660 // Replaces: ${self}:other ( << ${binary:Version})
661 NewDepends(D, V, VerStrIdx,
662 pkgCache::Dep::Less | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Replaces,
663 OldDepLast);
664 // Breaks: ${self}:other (!= ${binary:Version})
665 NewDepends(D, V, VerStrIdx,
666 pkgCache::Dep::NotEquals | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::DpkgBreaks,
667 OldDepLast);
668 } else {
669 // Conflicts: ${self}:other
670 NewDepends(D, V, 0,
671 pkgCache::Dep::NoOp | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Conflicts,
672 OldDepLast);
673 }
674 }
675 return true;
676 }
677 bool pkgCacheGenerator::AddImplicitDepends(pkgCache::VerIterator &V,
678 pkgCache::PkgIterator &D)
679 {
680 /* MultiArch handling introduces a lot of implicit Dependencies:
681 - MultiArch: same → Co-Installable if they have the same version
682 - All others conflict with all other group members */
683 map_pointer_t *OldDepLast = NULL;
684 bool const coInstall = ((V->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same);
685 if (coInstall == true)
686 {
687 map_stringitem_t const VerStrIdx = V->VerStr;
688 // Replaces: ${self}:other ( << ${binary:Version})
689 NewDepends(D, V, VerStrIdx,
690 pkgCache::Dep::Less | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Replaces,
691 OldDepLast);
692 // Breaks: ${self}:other (!= ${binary:Version})
693 NewDepends(D, V, VerStrIdx,
694 pkgCache::Dep::NotEquals | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::DpkgBreaks,
695 OldDepLast);
696 } else {
697 // Conflicts: ${self}:other
698 NewDepends(D, V, 0,
699 pkgCache::Dep::NoOp | pkgCache::Dep::MultiArchImplicit, pkgCache::Dep::Conflicts,
700 OldDepLast);
701 }
702 return true;
703 }
704
705 /*}}}*/
706 // CacheGenerator::NewFileVer - Create a new File<->Version association /*{{{*/
707 // ---------------------------------------------------------------------
708 /* */
709 bool pkgCacheGenerator::NewFileVer(pkgCache::VerIterator &Ver,
710 ListParser &List)
711 {
712 if (CurrentFile == 0)
713 return true;
714
715 // Get a structure
716 map_pointer_t const VerFile = AllocateInMap(sizeof(pkgCache::VerFile));
717 if (VerFile == 0)
718 return false;
719
720 pkgCache::VerFileIterator VF(Cache,Cache.VerFileP + VerFile);
721 VF->File = CurrentFile - Cache.PkgFileP;
722
723 // Link it to the end of the list
724 map_pointer_t *Last = &Ver->FileList;
725 for (pkgCache::VerFileIterator V = Ver.FileList(); V.end() == false; ++V)
726 Last = &V->NextFile;
727 VF->NextFile = *Last;
728 *Last = VF.Index();
729
730 VF->Offset = List.Offset();
731 VF->Size = List.Size();
732 if (Cache.HeaderP->MaxVerFileSize < VF->Size)
733 Cache.HeaderP->MaxVerFileSize = VF->Size;
734 Cache.HeaderP->VerFileCount++;
735
736 return true;
737 }
738 /*}}}*/
739 // CacheGenerator::NewVersion - Create a new Version /*{{{*/
740 // ---------------------------------------------------------------------
741 /* This puts a version structure in the linked list */
742 map_pointer_t pkgCacheGenerator::NewVersion(pkgCache::VerIterator &Ver,
743 const string &VerStr,
744 map_pointer_t const ParentPkg,
745 unsigned short const Hash,
746 map_pointer_t const Next)
747 {
748 // Get a structure
749 map_pointer_t const Version = AllocateInMap(sizeof(pkgCache::Version));
750 if (Version == 0)
751 return 0;
752
753 // Fill it in
754 Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
755 //Dynamic<pkgCache::VerIterator> DynV(Ver); // caller MergeListVersion already takes care of it
756 Ver->NextVer = Next;
757 Ver->ParentPkg = ParentPkg;
758 Ver->Hash = Hash;
759 Ver->ID = Cache.HeaderP->VersionCount++;
760
761 // try to find the version string in the group for reuse
762 pkgCache::PkgIterator Pkg = Ver.ParentPkg();
763 pkgCache::GrpIterator Grp = Pkg.Group();
764 if (Pkg.end() == false && Grp.end() == false)
765 {
766 for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
767 {
768 if (Pkg == P)
769 continue;
770 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
771 {
772 int const cmp = strcmp(V.VerStr(), VerStr.c_str());
773 if (cmp == 0)
774 {
775 Ver->VerStr = V->VerStr;
776 return Version;
777 }
778 else if (cmp < 0)
779 break;
780 }
781 }
782 }
783 // haven't found the version string, so create
784 map_stringitem_t const idxVerStr = StoreString(VERSIONNUMBER, VerStr);
785 if (unlikely(idxVerStr == 0))
786 return 0;
787 Ver->VerStr = idxVerStr;
788 return Version;
789 }
790 /*}}}*/
791 // CacheGenerator::NewFileDesc - Create a new File<->Desc association /*{{{*/
792 // ---------------------------------------------------------------------
793 /* */
794 bool pkgCacheGenerator::NewFileDesc(pkgCache::DescIterator &Desc,
795 ListParser &List)
796 {
797 if (CurrentFile == 0)
798 return true;
799
800 // Get a structure
801 map_pointer_t const DescFile = AllocateInMap(sizeof(pkgCache::DescFile));
802 if (DescFile == 0)
803 return false;
804
805 pkgCache::DescFileIterator DF(Cache,Cache.DescFileP + DescFile);
806 DF->File = CurrentFile - Cache.PkgFileP;
807
808 // Link it to the end of the list
809 map_pointer_t *Last = &Desc->FileList;
810 for (pkgCache::DescFileIterator D = Desc.FileList(); D.end() == false; ++D)
811 Last = &D->NextFile;
812
813 DF->NextFile = *Last;
814 *Last = DF.Index();
815
816 DF->Offset = List.Offset();
817 DF->Size = List.Size();
818 if (Cache.HeaderP->MaxDescFileSize < DF->Size)
819 Cache.HeaderP->MaxDescFileSize = DF->Size;
820 Cache.HeaderP->DescFileCount++;
821
822 return true;
823 }
824 /*}}}*/
825 // CacheGenerator::NewDescription - Create a new Description /*{{{*/
826 // ---------------------------------------------------------------------
827 /* This puts a description structure in the linked list */
828 map_pointer_t pkgCacheGenerator::NewDescription(pkgCache::DescIterator &Desc,
829 const string &Lang,
830 const MD5SumValue &md5sum,
831 map_stringitem_t const idxmd5str)
832 {
833 // Get a structure
834 map_pointer_t const Description = AllocateInMap(sizeof(pkgCache::Description));
835 if (Description == 0)
836 return 0;
837
838 // Fill it in
839 Desc = pkgCache::DescIterator(Cache,Cache.DescP + Description);
840 Desc->ID = Cache.HeaderP->DescriptionCount++;
841 map_stringitem_t const idxlanguage_code = StoreString(MIXED, Lang);
842 if (unlikely(idxlanguage_code == 0))
843 return 0;
844 Desc->language_code = idxlanguage_code;
845
846 if (idxmd5str != 0)
847 Desc->md5sum = idxmd5str;
848 else
849 {
850 map_stringitem_t const idxmd5sum = WriteStringInMap(md5sum.Value());
851 if (unlikely(idxmd5sum == 0))
852 return 0;
853 Desc->md5sum = idxmd5sum;
854 }
855
856 return Description;
857 }
858 /*}}}*/
859 // CacheGenerator::NewDepends - Create a dependency element /*{{{*/
860 // ---------------------------------------------------------------------
861 /* This creates a dependency element in the tree. It is linked to the
862 version and to the package that it is pointing to. */
863 bool pkgCacheGenerator::NewDepends(pkgCache::PkgIterator &Pkg,
864 pkgCache::VerIterator &Ver,
865 map_pointer_t const Version,
866 uint8_t const Op,
867 uint8_t const Type,
868 map_pointer_t* &OldDepLast)
869 {
870 void const * const oldMap = Map.Data();
871 // Get a structure
872 map_pointer_t const Dependency = AllocateInMap(sizeof(pkgCache::Dependency));
873 if (unlikely(Dependency == 0))
874 return false;
875
876 bool isDuplicate = false;
877 map_pointer_t DependencyData = 0;
878 map_pointer_t PreviousData = 0;
879 if (Pkg->RevDepends != 0)
880 {
881 pkgCache::Dependency const * const L = Cache.DepP + Pkg->RevDepends;
882 DependencyData = L->DependencyData;
883 do {
884 pkgCache::DependencyData const * const D = Cache.DepDataP + DependencyData;
885 if (Version > D->Version)
886 break;
887 if (D->Version == Version && D->Type == Type && D->CompareOp == Op)
888 {
889 isDuplicate = true;
890 break;
891 }
892 PreviousData = DependencyData;
893 DependencyData = D->NextData;
894 } while (DependencyData != 0);
895 }
896
897 if (isDuplicate == false)
898 {
899 DependencyData = AllocateInMap(sizeof(pkgCache::DependencyData));
900 if (unlikely(DependencyData == 0))
901 return false;
902 }
903
904 pkgCache::Dependency * Link = Cache.DepP + Dependency;
905 Link->ParentVer = Ver.Index();
906 Link->DependencyData = DependencyData;
907 Link->ID = Cache.HeaderP->DependsCount++;
908
909 pkgCache::DepIterator Dep(Cache, Link);
910 if (isDuplicate == false)
911 {
912 Dep->Type = Type;
913 Dep->CompareOp = Op;
914 Dep->Version = Version;
915 Dep->Package = Pkg.Index();
916 ++Cache.HeaderP->DependsDataCount;
917 if (PreviousData != 0)
918 {
919 pkgCache::DependencyData * const D = Cache.DepDataP + PreviousData;
920 Dep->NextData = D->NextData;
921 D->NextData = DependencyData;
922 }
923 else if (Pkg->RevDepends != 0)
924 {
925 pkgCache::Dependency const * const D = Cache.DepP + Pkg->RevDepends;
926 Dep->NextData = D->DependencyData;
927 }
928 }
929
930 if (isDuplicate == true || PreviousData != 0)
931 {
932 pkgCache::Dependency * const L = Cache.DepP + Pkg->RevDepends;
933 Link->NextRevDepends = L->NextRevDepends;
934 L->NextRevDepends = Dependency;
935 }
936 else
937 {
938 Link->NextRevDepends = Pkg->RevDepends;
939 Pkg->RevDepends = Dependency;
940 }
941
942
943 // Do we know where to link the Dependency to?
944 if (OldDepLast == NULL)
945 {
946 OldDepLast = &Ver->DependsList;
947 for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; ++D)
948 OldDepLast = &D->NextDepends;
949 } else if (oldMap != Map.Data())
950 OldDepLast += (map_pointer_t const * const) Map.Data() - (map_pointer_t const * const) oldMap;
951
952 Dep->NextDepends = *OldDepLast;
953 *OldDepLast = Dependency;
954 OldDepLast = &Dep->NextDepends;
955 return true;
956 }
957 /*}}}*/
958 // ListParser::NewDepends - Create the environment for a new dependency /*{{{*/
959 // ---------------------------------------------------------------------
960 /* This creates a Group and the Package to link this dependency to if
961 needed and handles also the caching of the old endpoint */
962 bool pkgCacheListParser::NewDepends(pkgCache::VerIterator &Ver,
963 const string &PackageName,
964 const string &Arch,
965 const string &Version,
966 uint8_t const Op,
967 uint8_t const Type)
968 {
969 pkgCache::GrpIterator Grp;
970 Dynamic<pkgCache::GrpIterator> DynGrp(Grp);
971 if (unlikely(Owner->NewGroup(Grp, PackageName) == false))
972 return false;
973
974 map_stringitem_t idxVersion = 0;
975 if (Version.empty() == false)
976 {
977 int const CmpOp = Op & 0x0F;
978 // =-deps are used (79:1) for lockstep on same-source packages (e.g. data-packages)
979 if (CmpOp == pkgCache::Dep::Equals && strcmp(Version.c_str(), Ver.VerStr()) == 0)
980 idxVersion = Ver->VerStr;
981
982 if (idxVersion == 0)
983 {
984 idxVersion = StoreString(pkgCacheGenerator::VERSIONNUMBER, Version);
985 if (unlikely(idxVersion == 0))
986 return false;
987 }
988 }
989
990 bool const isNegative = (Type == pkgCache::Dep::DpkgBreaks ||
991 Type == pkgCache::Dep::Conflicts ||
992 Type == pkgCache::Dep::Replaces);
993
994 pkgCache::PkgIterator Pkg;
995 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
996 if (isNegative == false || (Op & pkgCache::Dep::ArchSpecific) == pkgCache::Dep::ArchSpecific || Grp->FirstPackage == 0)
997 {
998 // Locate the target package
999 Pkg = Grp.FindPkg(Arch);
1000 if (Pkg.end() == true) {
1001 if (unlikely(Owner->NewPackage(Pkg, PackageName, Arch) == false))
1002 return false;
1003 }
1004
1005 /* Caching the old end point speeds up generation substantially */
1006 if (OldDepVer != Ver) {
1007 OldDepLast = NULL;
1008 OldDepVer = Ver;
1009 }
1010
1011 return Owner->NewDepends(Pkg, Ver, idxVersion, Op, Type, OldDepLast);
1012 }
1013 else
1014 {
1015 /* Caching the old end point speeds up generation substantially */
1016 if (OldDepVer != Ver) {
1017 OldDepLast = NULL;
1018 OldDepVer = Ver;
1019 }
1020
1021 for (Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
1022 {
1023 if (Owner->NewDepends(Pkg, Ver, idxVersion, Op, Type, OldDepLast) == false)
1024 return false;
1025 }
1026 }
1027 return true;
1028 }
1029 /*}}}*/
1030 // ListParser::NewProvides - Create a Provides element /*{{{*/
1031 bool pkgCacheListParser::NewProvides(pkgCache::VerIterator &Ver,
1032 const string &PkgName,
1033 const string &PkgArch,
1034 const string &Version,
1035 uint8_t const Flags)
1036 {
1037 pkgCache const &Cache = Owner->Cache;
1038
1039 // We do not add self referencing provides
1040 if (Ver.ParentPkg().Name() == PkgName && (PkgArch == Ver.ParentPkg().Arch() ||
1041 (PkgArch == "all" && strcmp((Cache.StrP + Cache.HeaderP->Architecture), Ver.ParentPkg().Arch()) == 0)))
1042 return true;
1043
1044 // Locate the target package
1045 pkgCache::PkgIterator Pkg;
1046 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
1047 if (unlikely(Owner->NewPackage(Pkg,PkgName, PkgArch) == false))
1048 return false;
1049
1050 map_stringitem_t idxProvideVersion = 0;
1051 if (Version.empty() == false) {
1052 idxProvideVersion = StoreString(pkgCacheGenerator::VERSIONNUMBER, Version);
1053 if (unlikely(idxProvideVersion == 0))
1054 return false;
1055 }
1056 return Owner->NewProvides(Ver, Pkg, idxProvideVersion, Flags);
1057 }
1058 bool pkgCacheGenerator::NewProvides(pkgCache::VerIterator &Ver,
1059 pkgCache::PkgIterator &Pkg,
1060 map_pointer_t const ProvideVersion,
1061 uint8_t const Flags)
1062 {
1063 // Get a structure
1064 map_pointer_t const Provides = AllocateInMap(sizeof(pkgCache::Provides));
1065 if (unlikely(Provides == 0))
1066 return false;
1067 ++Cache.HeaderP->ProvidesCount;
1068
1069 // Fill it in
1070 pkgCache::PrvIterator Prv(Cache,Cache.ProvideP + Provides,Cache.PkgP);
1071 Prv->Version = Ver.Index();
1072 Prv->ProvideVersion = ProvideVersion;
1073 Prv->Flags = Flags;
1074 Prv->NextPkgProv = Ver->ProvidesList;
1075 Ver->ProvidesList = Prv.Index();
1076
1077 // Link it to the package
1078 Prv->ParentPkg = Pkg.Index();
1079 Prv->NextProvides = Pkg->ProvidesList;
1080 Pkg->ProvidesList = Prv.Index();
1081 return true;
1082 }
1083 /*}}}*/
1084 // ListParser::NewProvidesAllArch - add provides for all architectures /*{{{*/
1085 bool pkgCacheListParser::NewProvidesAllArch(pkgCache::VerIterator &Ver, string const &Package,
1086 string const &Version, uint8_t const Flags) {
1087 pkgCache &Cache = Owner->Cache;
1088 pkgCache::GrpIterator Grp = Cache.FindGrp(Package);
1089 Dynamic<pkgCache::GrpIterator> DynGrp(Grp);
1090
1091 if (Grp.end() == true)
1092 return NewProvides(Ver, Package, Cache.NativeArch(), Version, Flags);
1093 else
1094 {
1095 map_stringitem_t idxProvideVersion = 0;
1096 if (Version.empty() == false) {
1097 idxProvideVersion = StoreString(pkgCacheGenerator::VERSIONNUMBER, Version);
1098 if (unlikely(idxProvideVersion == 0))
1099 return false;
1100 }
1101
1102 bool const isImplicit = (Flags & pkgCache::Flag::MultiArchImplicit) == pkgCache::Flag::MultiArchImplicit;
1103 bool const isArchSpecific = (Flags & pkgCache::Flag::ArchSpecific) == pkgCache::Flag::ArchSpecific;
1104 pkgCache::PkgIterator OwnerPkg = Ver.ParentPkg();
1105 Dynamic<pkgCache::PkgIterator> DynOwnerPkg(OwnerPkg);
1106 pkgCache::PkgIterator Pkg;
1107 Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
1108 for (Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
1109 {
1110 if (isImplicit && OwnerPkg == Pkg)
1111 continue;
1112 if (isArchSpecific == false && APT::Configuration::checkArchitecture(OwnerPkg.Arch()) == false)
1113 continue;
1114 if (Owner->NewProvides(Ver, Pkg, idxProvideVersion, Flags) == false)
1115 return false;
1116 }
1117 }
1118 return true;
1119 }
1120 /*}}}*/
1121 bool pkgCacheListParser::SameVersion(unsigned short const Hash, /*{{{*/
1122 pkgCache::VerIterator const &Ver)
1123 {
1124 return Hash == Ver->Hash;
1125 }
1126 /*}}}*/
1127 // CacheGenerator::SelectReleaseFile - Select the current release file the indexes belong to /*{{{*/
1128 bool pkgCacheGenerator::SelectReleaseFile(const string &File,const string &Site,
1129 unsigned long Flags)
1130 {
1131 if (File.empty() && Site.empty())
1132 {
1133 CurrentRlsFile = NULL;
1134 return true;
1135 }
1136
1137 // Get some space for the structure
1138 map_pointer_t const idxFile = AllocateInMap(sizeof(*CurrentRlsFile));
1139 if (unlikely(idxFile == 0))
1140 return false;
1141 CurrentRlsFile = Cache.RlsFileP + idxFile;
1142
1143 // Fill it in
1144 map_stringitem_t const idxFileName = WriteStringInMap(File);
1145 map_stringitem_t const idxSite = StoreString(MIXED, Site);
1146 if (unlikely(idxFileName == 0 || idxSite == 0))
1147 return false;
1148 CurrentRlsFile->FileName = idxFileName;
1149 CurrentRlsFile->Site = idxSite;
1150 CurrentRlsFile->NextFile = Cache.HeaderP->RlsFileList;
1151 CurrentRlsFile->Flags = Flags;
1152 CurrentRlsFile->ID = Cache.HeaderP->ReleaseFileCount;
1153 RlsFileName = File;
1154 Cache.HeaderP->RlsFileList = CurrentRlsFile - Cache.RlsFileP;
1155 Cache.HeaderP->ReleaseFileCount++;
1156
1157 return true;
1158 }
1159 /*}}}*/
1160 // CacheGenerator::SelectFile - Select the current file being parsed /*{{{*/
1161 // ---------------------------------------------------------------------
1162 /* This is used to select which file is to be associated with all newly
1163 added versions. The caller is responsible for setting the IMS fields. */
1164 bool pkgCacheGenerator::SelectFile(std::string const &File,
1165 pkgIndexFile const &Index,
1166 std::string const &Architecture,
1167 std::string const &Component,
1168 unsigned long const Flags)
1169 {
1170 // Get some space for the structure
1171 map_pointer_t const idxFile = AllocateInMap(sizeof(*CurrentFile));
1172 if (unlikely(idxFile == 0))
1173 return false;
1174 CurrentFile = Cache.PkgFileP + idxFile;
1175
1176 // Fill it in
1177 map_stringitem_t const idxFileName = WriteStringInMap(File);
1178 if (unlikely(idxFileName == 0))
1179 return false;
1180 CurrentFile->FileName = idxFileName;
1181 CurrentFile->NextFile = Cache.HeaderP->FileList;
1182 CurrentFile->ID = Cache.HeaderP->PackageFileCount;
1183 map_stringitem_t const idxIndexType = StoreString(MIXED, Index.GetType()->Label);
1184 if (unlikely(idxIndexType == 0))
1185 return false;
1186 CurrentFile->IndexType = idxIndexType;
1187 if (Architecture.empty())
1188 CurrentFile->Architecture = 0;
1189 else
1190 {
1191 map_stringitem_t const arch = StoreString(pkgCacheGenerator::MIXED, Architecture);
1192 if (unlikely(arch == 0))
1193 return false;
1194 CurrentFile->Architecture = arch;
1195 }
1196 map_stringitem_t const component = StoreString(pkgCacheGenerator::MIXED, Component);
1197 if (unlikely(component == 0))
1198 return false;
1199 CurrentFile->Component = component;
1200 CurrentFile->Flags = Flags;
1201 if (CurrentRlsFile != NULL)
1202 CurrentFile->Release = CurrentRlsFile - Cache.RlsFileP;
1203 else
1204 CurrentFile->Release = 0;
1205 PkgFileName = File;
1206 Cache.HeaderP->FileList = CurrentFile - Cache.PkgFileP;
1207 Cache.HeaderP->PackageFileCount++;
1208
1209 if (Progress != 0)
1210 Progress->SubProgress(Index.Size());
1211 return true;
1212 }
1213 /*}}}*/
1214 // CacheGenerator::WriteUniqueString - Insert a unique string /*{{{*/
1215 // ---------------------------------------------------------------------
1216 /* This is used to create handles to strings. Given the same text it
1217 always returns the same number */
1218 map_stringitem_t pkgCacheGenerator::StoreString(enum StringType const type, const char *S,
1219 unsigned int Size)
1220 {
1221 std::string const key(S, Size);
1222
1223 std::map<std::string,map_stringitem_t> * strings;
1224 switch(type) {
1225 case MIXED: strings = &strMixed; break;
1226 case PKGNAME: strings = &strPkgNames; break;
1227 case VERSIONNUMBER: strings = &strVersions; break;
1228 case SECTION: strings = &strSections; break;
1229 default: _error->Fatal("Unknown enum type used for string storage of '%s'", key.c_str()); return 0;
1230 }
1231
1232 std::map<std::string,map_stringitem_t>::const_iterator const item = strings->find(key);
1233 if (item != strings->end())
1234 return item->second;
1235
1236 map_stringitem_t const idxString = WriteStringInMap(S,Size);
1237 strings->insert(std::make_pair(key, idxString));
1238 return idxString;
1239 }
1240 /*}}}*/
1241 // CheckValidity - Check that a cache is up-to-date /*{{{*/
1242 // ---------------------------------------------------------------------
1243 /* This just verifies that each file in the list of index files exists,
1244 has matching attributes with the cache and the cache does not have
1245 any extra files. */
1246 static bool CheckValidity(const string &CacheFile,
1247 pkgSourceList &List,
1248 FileIterator const Start,
1249 FileIterator const End,
1250 MMap **OutMap = 0)
1251 {
1252 bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
1253 // No file, certainly invalid
1254 if (CacheFile.empty() == true || FileExists(CacheFile) == false)
1255 {
1256 if (Debug == true)
1257 std::clog << "CacheFile " << CacheFile << " doesn't exist" << std::endl;
1258 return false;
1259 }
1260
1261 if (List.GetLastModifiedTime() > GetModificationTime(CacheFile))
1262 {
1263 if (Debug == true)
1264 std::clog << "sources.list is newer than the cache" << std::endl;
1265 return false;
1266 }
1267
1268 // Map it
1269 FileFd CacheF(CacheFile,FileFd::ReadOnly);
1270 std::unique_ptr<MMap> Map(new MMap(CacheF,0));
1271 pkgCache Cache(Map.get());
1272 if (_error->PendingError() == true || Map->Size() == 0)
1273 {
1274 if (Debug == true)
1275 std::clog << "Errors are pending or Map is empty() for " << CacheFile << std::endl;
1276 _error->Discard();
1277 return false;
1278 }
1279
1280 std::unique_ptr<bool[]> RlsVisited(new bool[Cache.HeaderP->ReleaseFileCount]);
1281 memset(RlsVisited.get(),0,sizeof(RlsVisited[0])*Cache.HeaderP->ReleaseFileCount);
1282 std::vector<pkgIndexFile *> Files;
1283 for (pkgSourceList::const_iterator i = List.begin(); i != List.end(); ++i)
1284 {
1285 if (Debug == true)
1286 std::clog << "Checking RlsFile " << (*i)->Describe() << ": ";
1287 pkgCache::RlsFileIterator const RlsFile = (*i)->FindInCache(Cache, true);
1288 if (RlsFile.end() == true)
1289 {
1290 if (Debug == true)
1291 std::clog << "FindInCache returned end-Pointer" << std::endl;
1292 return false;
1293 }
1294
1295 RlsVisited[RlsFile->ID] = true;
1296 if (Debug == true)
1297 std::clog << "with ID " << RlsFile->ID << " is valid" << std::endl;
1298
1299 std::vector <pkgIndexFile *> const * const Indexes = (*i)->GetIndexFiles();
1300 std::copy_if(Indexes->begin(), Indexes->end(), std::back_inserter(Files),
1301 [](pkgIndexFile const * const I) { return I->HasPackages(); });
1302 }
1303 for (unsigned I = 0; I != Cache.HeaderP->ReleaseFileCount; ++I)
1304 if (RlsVisited[I] == false)
1305 {
1306 if (Debug == true)
1307 std::clog << "RlsFile with ID" << I << " wasn't visited" << std::endl;
1308 return false;
1309 }
1310
1311 std::copy(Start, End, std::back_inserter(Files));
1312
1313 /* Now we check every index file, see if it is in the cache,
1314 verify the IMS data and check that it is on the disk too.. */
1315 std::unique_ptr<bool[]> Visited(new bool[Cache.HeaderP->PackageFileCount]);
1316 memset(Visited.get(),0,sizeof(Visited[0])*Cache.HeaderP->PackageFileCount);
1317 for (std::vector<pkgIndexFile *>::const_reverse_iterator PkgFile = Files.rbegin(); PkgFile != Files.rend(); ++PkgFile)
1318 {
1319 if (Debug == true)
1320 std::clog << "Checking PkgFile " << (*PkgFile)->Describe() << ": ";
1321 if ((*PkgFile)->Exists() == false)
1322 {
1323 if (Debug == true)
1324 std::clog << "file doesn't exist" << std::endl;
1325 continue;
1326 }
1327
1328 // FindInCache is also expected to do an IMS check.
1329 pkgCache::PkgFileIterator File = (*PkgFile)->FindInCache(Cache);
1330 if (File.end() == true)
1331 {
1332 if (Debug == true)
1333 std::clog << "FindInCache returned end-Pointer" << std::endl;
1334 return false;
1335 }
1336
1337 Visited[File->ID] = true;
1338 if (Debug == true)
1339 std::clog << "with ID " << File->ID << " is valid" << std::endl;
1340 }
1341
1342 for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++)
1343 if (Visited[I] == false)
1344 {
1345 if (Debug == true)
1346 std::clog << "PkgFile with ID" << I << " wasn't visited" << std::endl;
1347 return false;
1348 }
1349
1350 if (_error->PendingError() == true)
1351 {
1352 if (Debug == true)
1353 {
1354 std::clog << "Validity failed because of pending errors:" << std::endl;
1355 _error->DumpErrors();
1356 }
1357 _error->Discard();
1358 return false;
1359 }
1360
1361 if (OutMap != 0)
1362 *OutMap = Map.release();
1363 return true;
1364 }
1365 /*}}}*/
1366 // ComputeSize - Compute the total size of a bunch of files /*{{{*/
1367 // ---------------------------------------------------------------------
1368 /* Size is kind of an abstract notion that is only used for the progress
1369 meter */
1370 static map_filesize_t ComputeSize(pkgSourceList const * const List, FileIterator Start,FileIterator End)
1371 {
1372 map_filesize_t TotalSize = 0;
1373 if (List != NULL)
1374 {
1375 for (pkgSourceList::const_iterator i = List->begin(); i != List->end(); ++i)
1376 {
1377 std::vector <pkgIndexFile *> *Indexes = (*i)->GetIndexFiles();
1378 for (std::vector<pkgIndexFile *>::const_iterator j = Indexes->begin(); j != Indexes->end(); ++j)
1379 if ((*j)->HasPackages() == true)
1380 TotalSize += (*j)->Size();
1381 }
1382 }
1383
1384 for (; Start < End; ++Start)
1385 {
1386 if ((*Start)->HasPackages() == false)
1387 continue;
1388 TotalSize += (*Start)->Size();
1389 }
1390 return TotalSize;
1391 }
1392 /*}}}*/
1393 // BuildCache - Merge the list of index files into the cache /*{{{*/
1394 static bool BuildCache(pkgCacheGenerator &Gen,
1395 OpProgress * const Progress,
1396 map_filesize_t &CurrentSize,map_filesize_t TotalSize,
1397 pkgSourceList const * const List,
1398 FileIterator const Start, FileIterator const End)
1399 {
1400 std::vector<pkgIndexFile *> Files;
1401 bool mergeFailure = false;
1402
1403 auto const indexFileMerge = [&](pkgIndexFile * const I) {
1404 if (I->HasPackages() == false || mergeFailure)
1405 return;
1406
1407 if (I->Exists() == false)
1408 return;
1409
1410 if (I->FindInCache(Gen.GetCache()).end() == false)
1411 {
1412 _error->Warning("Duplicate sources.list entry %s",
1413 I->Describe().c_str());
1414 return;
1415 }
1416
1417 map_filesize_t const Size = I->Size();
1418 if (Progress != NULL)
1419 Progress->OverallProgress(CurrentSize, TotalSize, Size, _("Reading package lists"));
1420 CurrentSize += Size;
1421
1422 if (I->Merge(Gen,Progress) == false)
1423 mergeFailure = true;
1424 };
1425
1426 if (List != NULL)
1427 {
1428 for (pkgSourceList::const_iterator i = List->begin(); i != List->end(); ++i)
1429 {
1430 if ((*i)->FindInCache(Gen.GetCache(), false).end() == false)
1431 {
1432 _error->Warning("Duplicate sources.list entry %s",
1433 (*i)->Describe().c_str());
1434 continue;
1435 }
1436
1437 if ((*i)->Merge(Gen, Progress) == false)
1438 return false;
1439
1440 std::vector <pkgIndexFile *> *Indexes = (*i)->GetIndexFiles();
1441 if (Indexes != NULL)
1442 std::for_each(Indexes->begin(), Indexes->end(), indexFileMerge);
1443 if (mergeFailure)
1444 return false;
1445 }
1446 }
1447
1448 if (Start != End)
1449 {
1450 Gen.SelectReleaseFile("", "");
1451 std::for_each(Start, End, indexFileMerge);
1452 if (mergeFailure)
1453 return false;
1454 }
1455 return true;
1456 }
1457 /*}}}*/
1458 // CacheGenerator::MakeStatusCache - Construct the status cache /*{{{*/
1459 // ---------------------------------------------------------------------
1460 /* This makes sure that the status cache (the cache that has all
1461 index files from the sources list and all local ones) is ready
1462 to be mmaped. If OutMap is not zero then a MMap object representing
1463 the cache will be stored there. This is pretty much mandetory if you
1464 are using AllowMem. AllowMem lets the function be run as non-root
1465 where it builds the cache 'fast' into a memory buffer. */
1466 static DynamicMMap* CreateDynamicMMap(FileFd * const CacheF, unsigned long Flags)
1467 {
1468 map_filesize_t const MapStart = _config->FindI("APT::Cache-Start", 24*1024*1024);
1469 map_filesize_t const MapGrow = _config->FindI("APT::Cache-Grow", 1*1024*1024);
1470 map_filesize_t const MapLimit = _config->FindI("APT::Cache-Limit", 0);
1471 Flags |= MMap::Moveable;
1472 if (_config->FindB("APT::Cache-Fallback", false) == true)
1473 Flags |= MMap::Fallback;
1474 if (CacheF != NULL)
1475 return new DynamicMMap(*CacheF, Flags, MapStart, MapGrow, MapLimit);
1476 else
1477 return new DynamicMMap(Flags, MapStart, MapGrow, MapLimit);
1478 }
1479 static bool writeBackMMapToFile(pkgCacheGenerator * const Gen, DynamicMMap * const Map,
1480 std::string const &FileName)
1481 {
1482 FileFd SCacheF(FileName, FileFd::WriteAtomic);
1483 if (_error->PendingError() == true)
1484 return false;
1485
1486 fchmod(SCacheF.Fd(),0644);
1487
1488 // Write out the main data
1489 if (SCacheF.Write(Map->Data(),Map->Size()) == false)
1490 return _error->Error(_("IO Error saving source cache"));
1491 SCacheF.Sync();
1492
1493 // Write out the proper header
1494 Gen->GetCache().HeaderP->Dirty = false;
1495 if (SCacheF.Seek(0) == false ||
1496 SCacheF.Write(Map->Data(),sizeof(*Gen->GetCache().HeaderP)) == false)
1497 return _error->Error(_("IO Error saving source cache"));
1498 Gen->GetCache().HeaderP->Dirty = true;
1499 SCacheF.Sync();
1500 return true;
1501 }
1502 static bool loadBackMMapFromFile(std::unique_ptr<pkgCacheGenerator> &Gen,
1503 std::unique_ptr<DynamicMMap> &Map, OpProgress * const Progress, std::string const &FileName)
1504 {
1505 Map.reset(CreateDynamicMMap(NULL, 0));
1506 FileFd CacheF(FileName, FileFd::ReadOnly);
1507 map_pointer_t const alloc = Map->RawAllocate(CacheF.Size());
1508 if ((alloc == 0 && _error->PendingError())
1509 || CacheF.Read((unsigned char *)Map->Data() + alloc,
1510 CacheF.Size()) == false)
1511 return false;
1512 Gen.reset(new pkgCacheGenerator(Map.get(),Progress));
1513 return true;
1514 }
1515 APT_DEPRECATED bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress,
1516 MMap **OutMap, bool AllowMem)
1517 { return pkgCacheGenerator::MakeStatusCache(List, &Progress, OutMap, AllowMem); }
1518 bool pkgCacheGenerator::MakeStatusCache(pkgSourceList &List,OpProgress *Progress,
1519 MMap **OutMap,bool)
1520 {
1521 // FIXME: deprecate the ignored AllowMem parameter
1522 bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
1523
1524 std::vector<pkgIndexFile *> Files;
1525 if (_system->AddStatusFiles(Files) == false)
1526 return false;
1527
1528 // Decide if we can write to the files..
1529 string const CacheFile = _config->FindFile("Dir::Cache::pkgcache");
1530 string const SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
1531
1532 // ensure the cache directory exists
1533 if (CacheFile.empty() == false || SrcCacheFile.empty() == false)
1534 {
1535 string dir = _config->FindDir("Dir::Cache");
1536 size_t const len = dir.size();
1537 if (len > 5 && dir.find("/apt/", len - 6, 5) == len - 5)
1538 dir = dir.substr(0, len - 5);
1539 if (CacheFile.empty() == false)
1540 CreateDirectory(dir, flNotFile(CacheFile));
1541 if (SrcCacheFile.empty() == false)
1542 CreateDirectory(dir, flNotFile(SrcCacheFile));
1543 }
1544
1545 if (Progress != NULL)
1546 Progress->OverallProgress(0,1,1,_("Reading package lists"));
1547
1548 bool pkgcache_fine = false;
1549 bool srcpkgcache_fine = false;
1550 bool volatile_fine = List.GetVolatileFiles().empty();
1551
1552 if (CheckValidity(CacheFile, List, Files.begin(), Files.end(), volatile_fine ? OutMap : NULL) == true)
1553 {
1554 if (Debug == true)
1555 std::clog << "pkgcache.bin is valid - no need to build any cache" << std::endl;
1556 pkgcache_fine = true;
1557 srcpkgcache_fine = true;
1558 }
1559 if (pkgcache_fine == false)
1560 {
1561 if (CheckValidity(SrcCacheFile, List, Files.end(), Files.end()) == true)
1562 {
1563 if (Debug == true)
1564 std::clog << "srcpkgcache.bin is valid - it can be reused" << std::endl;
1565 srcpkgcache_fine = true;
1566 }
1567 }
1568
1569 if (volatile_fine == true && srcpkgcache_fine == true && pkgcache_fine == true)
1570 {
1571 if (Progress != NULL)
1572 Progress->OverallProgress(1,1,1,_("Reading package lists"));
1573 return true;
1574 }
1575
1576 bool Writeable = false;
1577 if (srcpkgcache_fine == false || pkgcache_fine == false)
1578 {
1579 if (CacheFile.empty() == false)
1580 Writeable = access(flNotFile(CacheFile).c_str(),W_OK) == 0;
1581 else if (SrcCacheFile.empty() == false)
1582 Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0;
1583
1584 if (Debug == true)
1585 std::clog << "Do we have write-access to the cache files? " << (Writeable ? "YES" : "NO") << std::endl;
1586 }
1587
1588 // At this point we know we need to construct something, so get storage ready
1589 std::unique_ptr<DynamicMMap> Map(CreateDynamicMMap(NULL, 0));
1590 if (Debug == true)
1591 std::clog << "Open memory Map (not filebased)" << std::endl;
1592
1593 std::unique_ptr<pkgCacheGenerator> Gen{nullptr};
1594 map_filesize_t CurrentSize = 0;
1595 std::vector<pkgIndexFile*> VolatileFiles = List.GetVolatileFiles();
1596 map_filesize_t TotalSize = ComputeSize(NULL, VolatileFiles.begin(), VolatileFiles.end());
1597 if (srcpkgcache_fine == true && pkgcache_fine == false)
1598 {
1599 if (Debug == true)
1600 std::clog << "srcpkgcache.bin was valid - populate MMap with it" << std::endl;
1601 if (loadBackMMapFromFile(Gen, Map, Progress, SrcCacheFile) == false)
1602 return false;
1603 srcpkgcache_fine = true;
1604 TotalSize += ComputeSize(NULL, Files.begin(), Files.end());
1605 }
1606 else if (srcpkgcache_fine == false)
1607 {
1608 if (Debug == true)
1609 std::clog << "srcpkgcache.bin is NOT valid - rebuild" << std::endl;
1610 Gen.reset(new pkgCacheGenerator(Map.get(),Progress));
1611
1612 TotalSize += ComputeSize(&List, Files.begin(),Files.end());
1613 if (BuildCache(*Gen, Progress, CurrentSize, TotalSize, &List,
1614 Files.end(),Files.end()) == false)
1615 return false;
1616
1617 if (Writeable == true && SrcCacheFile.empty() == false)
1618 if (writeBackMMapToFile(Gen.get(), Map.get(), SrcCacheFile) == false)
1619 return false;
1620 }
1621
1622 if (pkgcache_fine == false)
1623 {
1624 if (Debug == true)
1625 std::clog << "Building status cache in pkgcache.bin now" << std::endl;
1626 if (BuildCache(*Gen, Progress, CurrentSize, TotalSize, NULL,
1627 Files.begin(), Files.end()) == false)
1628 return false;
1629
1630 if (Writeable == true && CacheFile.empty() == false)
1631 if (writeBackMMapToFile(Gen.get(), Map.get(), CacheFile) == false)
1632 return false;
1633 }
1634
1635 if (Debug == true)
1636 std::clog << "Caches done. Now bring in the volatile files (if any)" << std::endl;
1637
1638 if (volatile_fine == false)
1639 {
1640 if (Gen == nullptr)
1641 {
1642 if (Debug == true)
1643 std::clog << "Populate new MMap with cachefile contents" << std::endl;
1644 if (loadBackMMapFromFile(Gen, Map, Progress, CacheFile) == false)
1645 return false;
1646 }
1647
1648 Files = List.GetVolatileFiles();
1649 if (BuildCache(*Gen, Progress, CurrentSize, TotalSize, NULL,
1650 Files.begin(), Files.end()) == false)
1651 return false;
1652 }
1653
1654 if (OutMap != nullptr)
1655 *OutMap = Map.release();
1656
1657 if (Debug == true)
1658 std::clog << "Everything is ready for shipping" << std::endl;
1659 return true;
1660 }
1661 /*}}}*/
1662 // CacheGenerator::MakeOnlyStatusCache - Build only a status files cache/*{{{*/
1663 // ---------------------------------------------------------------------
1664 /* */
1665 APT_DEPRECATED bool pkgMakeOnlyStatusCache(OpProgress &Progress,DynamicMMap **OutMap)
1666 { return pkgCacheGenerator::MakeOnlyStatusCache(&Progress, OutMap); }
1667 bool pkgCacheGenerator::MakeOnlyStatusCache(OpProgress *Progress,DynamicMMap **OutMap)
1668 {
1669 std::vector<pkgIndexFile *> Files;
1670 if (_system->AddStatusFiles(Files) == false)
1671 return false;
1672
1673 std::unique_ptr<DynamicMMap> Map(CreateDynamicMMap(NULL, 0));
1674 map_filesize_t CurrentSize = 0;
1675 map_filesize_t TotalSize = 0;
1676
1677 TotalSize = ComputeSize(NULL, Files.begin(), Files.end());
1678
1679 // Build the status cache
1680 if (Progress != NULL)
1681 Progress->OverallProgress(0,1,1,_("Reading package lists"));
1682 pkgCacheGenerator Gen(Map.get(),Progress);
1683 if (_error->PendingError() == true)
1684 return false;
1685 if (BuildCache(Gen,Progress,CurrentSize,TotalSize, NULL,
1686 Files.begin(), Files.end()) == false)
1687 return false;
1688
1689 if (_error->PendingError() == true)
1690 return false;
1691 *OutMap = Map.release();
1692
1693 return true;
1694 }
1695 /*}}}*/
1696 // IsDuplicateDescription /*{{{*/
1697 static bool IsDuplicateDescription(pkgCache::DescIterator Desc,
1698 MD5SumValue const &CurMd5, std::string const &CurLang)
1699 {
1700 // Descriptions in the same link-list have all the same md5
1701 if (Desc.end() == true || MD5SumValue(Desc.md5()) != CurMd5)
1702 return false;
1703 for (; Desc.end() == false; ++Desc)
1704 if (Desc.LanguageCode() == CurLang)
1705 return true;
1706 return false;
1707 }
1708 /*}}}*/
1709
1710 pkgCacheListParser::pkgCacheListParser() : Owner(NULL), OldDepLast(NULL), d(NULL) {}
1711 pkgCacheListParser::~pkgCacheListParser() {}