]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
Merge branch 'debian/experimental' into feature/srv-records
[apt.git] / ftparchive / writer.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
5200ec6f 3// $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
b2e465d6
AL
4/* ######################################################################
5
6 Writer
7
8 The file writer classes. These write various types of output, sources,
9 packages and contents.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
ea542140
DK
14#include <config.h>
15
b2e465d6 16#include <apt-pkg/configuration.h>
b2e465d6 17#include <apt-pkg/deblistparser.h>
453b82a3 18#include <apt-pkg/error.h>
f1828b69
DK
19#include <apt-pkg/fileutl.h>
20#include <apt-pkg/gpgv.h>
453b82a3
DK
21#include <apt-pkg/hashes.h>
22#include <apt-pkg/md5.h>
23#include <apt-pkg/strutl.h>
24#include <apt-pkg/debfile.h>
25#include <apt-pkg/pkgcache.h>
26#include <apt-pkg/sha1.h>
27#include <apt-pkg/sha2.h>
28#include <apt-pkg/tagfile.h>
b2e465d6 29
453b82a3
DK
30#include <ctype.h>
31#include <fnmatch.h>
32#include <ftw.h>
33#include <locale.h>
34#include <string.h>
35#include <sys/stat.h>
b2e465d6
AL
36#include <sys/types.h>
37#include <unistd.h>
98953965 38#include <ctime>
8c58f506 39#include <iostream>
bf99a6d3 40#include <sstream>
4f333a8b 41#include <memory>
453b82a3 42#include <utility>
ea542140 43
453b82a3 44#include "apt-ftparchive.h"
ea542140 45#include "writer.h"
b2e465d6 46#include "cachedb.h"
b2e465d6 47#include "multicompress.h"
ea542140
DK
48
49#include <apti18n.h>
b2e465d6 50 /*}}}*/
8c58f506 51using namespace std;
b2e465d6
AL
52FTWScanner *FTWScanner::Owner;
53
a311fb96
DK
54// ConfigToDoHashes - which hashes to generate /*{{{*/
55static void SingleConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf, unsigned int const Flag)
56{
57 if (_config->FindB(Conf, true) == true)
58 DoHashes |= Flag;
59 else
60 DoHashes &= ~Flag;
61}
62static void ConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf)
63{
64 SingleConfigToDoHashes(DoHashes, Conf + "::MD5", Hashes::MD5SUM);
65 SingleConfigToDoHashes(DoHashes, Conf + "::SHA1", Hashes::SHA1SUM);
66 SingleConfigToDoHashes(DoHashes, Conf + "::SHA256", Hashes::SHA256SUM);
67 SingleConfigToDoHashes(DoHashes, Conf + "::SHA512", Hashes::SHA512SUM);
68}
69 /*}}}*/
64177f17 70
b2e465d6
AL
71// FTWScanner::FTWScanner - Constructor /*{{{*/
72// ---------------------------------------------------------------------
73/* */
88593886 74FTWScanner::FTWScanner(FileFd * const GivenOutput, string const &Arch): Arch(Arch), DoHashes(~0)
b2e465d6 75{
88593886
DK
76 if (GivenOutput == NULL)
77 {
78 Output = new FileFd;
79 Output->OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false);
80 }
81 else
82 Output = GivenOutput;
b2e465d6
AL
83 ErrorPrinted = false;
84 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
a311fb96 85 ConfigToDoHashes(DoHashes, "APT::FTPArchive");
b2e465d6
AL
86}
87 /*}}}*/
88// FTWScanner::Scanner - FTW Scanner /*{{{*/
89// ---------------------------------------------------------------------
65512241 90/* This is the FTW scanner, it processes each directory element in the
b2e465d6 91 directory tree. */
65512241 92int FTWScanner::ScannerFTW(const char *File,const struct stat * /*sb*/,int Flag)
b2e465d6
AL
93{
94 if (Flag == FTW_DNR)
95 {
96 Owner->NewLine(1);
dc738e7a 97 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
b2e465d6
AL
98 }
99 if (Flag == FTW_NS)
100 {
101 Owner->NewLine(1);
dc738e7a 102 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
b2e465d6
AL
103 }
104 if (Flag != FTW_F)
105 return 0;
106
cde41ae8
MV
107 return ScannerFile(File, true);
108}
109 /*}}}*/
110// FTWScanner::ScannerFile - File Scanner /*{{{*/
111// ---------------------------------------------------------------------
112/* */
9209ec47 113int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
cde41ae8 114{
98953965 115 const char *LastComponent = strrchr(File, '/');
ab3846c0
MV
116 char *RealPath = NULL;
117
98953965
AL
118 if (LastComponent == NULL)
119 LastComponent = File;
120 else
121 LastComponent++;
122
9209ec47 123 vector<string>::const_iterator I;
98953965
AL
124 for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
125 {
126 if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
127 break;
128 }
129 if (I == Owner->Patterns.end())
b2e465d6
AL
130 return 0;
131
132 /* Process it. If the file is a link then resolve it into an absolute
133 name.. This works best if the directory components the scanner are
134 given are not links themselves. */
135 char Jnk[2];
136 Owner->OriginalPath = File;
ab3846c0 137 if (ReadLink &&
cde41ae8 138 readlink(File,Jnk,sizeof(Jnk)) != -1 &&
ab3846c0
MV
139 (RealPath = realpath(File,NULL)) != 0)
140 {
141 Owner->DoPackage(RealPath);
142 free(RealPath);
143 }
b2e465d6
AL
144 else
145 Owner->DoPackage(File);
146
147 if (_error->empty() == false)
148 {
149 // Print any errors or warnings found
150 string Err;
151 bool SeenPath = false;
152 while (_error->empty() == false)
153 {
154 Owner->NewLine(1);
155
9209ec47 156 bool const Type = _error->PopMessage(Err);
b2e465d6 157 if (Type == true)
dc738e7a 158 cerr << _("E: ") << Err << endl;
b2e465d6 159 else
dc738e7a 160 cerr << _("W: ") << Err << endl;
b2e465d6
AL
161
162 if (Err.find(File) != string::npos)
163 SeenPath = true;
164 }
165
166 if (SeenPath == false)
dc738e7a 167 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
b2e465d6
AL
168 return 0;
169 }
170
171 return 0;
172}
173 /*}}}*/
174// FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
175// ---------------------------------------------------------------------
176/* */
9209ec47 177bool FTWScanner::RecursiveScan(string const &Dir)
b2e465d6 178{
ab3846c0 179 char *RealPath = NULL;
b2e465d6
AL
180 /* If noprefix is set then jam the scan root in, so we don't generate
181 link followed paths out of control */
182 if (InternalPrefix.empty() == true)
183 {
ab3846c0 184 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
dc738e7a 185 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
ab3846c0
MV
186 InternalPrefix = RealPath;
187 free(RealPath);
b2e465d6
AL
188 }
189
190 // Do recursive directory searching
191 Owner = this;
9209ec47 192 int const Res = ftw(Dir.c_str(),ScannerFTW,30);
b2e465d6
AL
193
194 // Error treewalking?
195 if (Res != 0)
196 {
197 if (_error->PendingError() == false)
dc738e7a 198 _error->Errno("ftw",_("Tree walking failed"));
b2e465d6
AL
199 return false;
200 }
201
202 return true;
203}
204 /*}}}*/
205// FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
206// ---------------------------------------------------------------------
207/* This is an alternative to using FTW to locate files, it reads the list
208 of files from another file. */
9209ec47 209bool FTWScanner::LoadFileList(string const &Dir, string const &File)
b2e465d6 210{
ab3846c0 211 char *RealPath = NULL;
b2e465d6
AL
212 /* If noprefix is set then jam the scan root in, so we don't generate
213 link followed paths out of control */
214 if (InternalPrefix.empty() == true)
215 {
ab3846c0 216 if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
dc738e7a 217 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
b2e465d6 218 InternalPrefix = RealPath;
ab3846c0 219 free(RealPath);
b2e465d6
AL
220 }
221
222 Owner = this;
223 FILE *List = fopen(File.c_str(),"r");
224 if (List == 0)
dc738e7a 225 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
b2e465d6
AL
226
227 /* We are a tad tricky here.. We prefix the buffer with the directory
228 name, that way if we need a full path with just use line.. Sneaky and
229 fully evil. */
230 char Line[1000];
231 char *FileStart;
232 if (Dir.empty() == true || Dir.end()[-1] != '/')
233 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
234 else
235 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
236 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
237 {
238 char *FileName = _strstrip(FileStart);
239 if (FileName[0] == 0)
240 continue;
241
242 if (FileName[0] != '/')
243 {
244 if (FileName != FileStart)
245 memmove(FileStart,FileName,strlen(FileStart));
246 FileName = Line;
247 }
248
cde41ae8 249#if 0
b2e465d6
AL
250 struct stat St;
251 int Flag = FTW_F;
252 if (stat(FileName,&St) != 0)
253 Flag = FTW_NS;
cde41ae8 254#endif
b2e465d6 255
cde41ae8 256 if (ScannerFile(FileName, false) != 0)
b2e465d6
AL
257 break;
258 }
259
260 fclose(List);
261 return true;
262}
263 /*}}}*/
264// FTWScanner::Delink - Delink symlinks /*{{{*/
265// ---------------------------------------------------------------------
266/* */
267bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
650faab0
DK
268 unsigned long long &DeLinkBytes,
269 unsigned long long const &FileSize)
b2e465d6
AL
270{
271 // See if this isn't an internaly prefix'd file name.
272 if (InternalPrefix.empty() == false &&
273 InternalPrefix.length() < FileName.length() &&
274 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
275 InternalPrefix.begin(),InternalPrefix.end()) != 0)
276 {
277 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
278 {
279 // Tidy up the display
280 if (DeLinkBytes == 0)
281 cout << endl;
282
283 NewLine(1);
dc738e7a 284 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
cde41ae8 285 SizeToStr(FileSize).c_str());
dc738e7a 286 c1out << flush;
b2e465d6
AL
287
288 if (NoLinkAct == false)
289 {
290 char OldLink[400];
291 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
dc738e7a 292 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
b2e465d6
AL
293 else
294 {
295 if (unlink(OriginalPath) != 0)
dc738e7a 296 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
b2e465d6
AL
297 else
298 {
299 if (link(FileName.c_str(),OriginalPath) != 0)
300 {
301 // Panic! Restore the symlink
f52037d6
MV
302 if (symlink(OldLink,OriginalPath) != 0)
303 _error->Errno("symlink", "failed to restore symlink");
dc738e7a 304 return _error->Errno("link",_("*** Failed to link %s to %s"),
b2e465d6
AL
305 FileName.c_str(),
306 OriginalPath);
307 }
308 }
309 }
310 }
311
cde41ae8 312 DeLinkBytes += FileSize;
b2e465d6 313 if (DeLinkBytes/1024 >= DeLinkLimit)
dc738e7a 314 ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
b2e465d6
AL
315 }
316
317 FileName = OriginalPath;
318 }
319
320 return true;
321}
322 /*}}}*/
b2e465d6
AL
323
324// PackagesWriter::PackagesWriter - Constructor /*{{{*/
325// ---------------------------------------------------------------------
326/* */
88593886 327PackagesWriter::PackagesWriter(FileFd * const GivenOutput, string const &DB,string const &Overrides,string const &ExtOverrides,
31981076 328 string const &Arch) :
88593886 329 FTWScanner(GivenOutput, Arch), Db(DB), Stats(Db.Stats), TransWriter(NULL)
b2e465d6 330{
31981076 331 SetExts(".deb .udeb");
b2e465d6 332 DeLinkLimit = 0;
3cb3fe76 333
b2e465d6 334 // Process the command line options
a311fb96 335 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Packages");
ff574e76 336 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
337 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
338 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
9c24493f 339 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
b2e465d6
AL
340
341 if (Db.Loaded() == false)
342 DoContents = false;
66905344 343
b2e465d6
AL
344 // Read the override file
345 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
346 return;
347 else
348 NoOverride = true;
64177f17
AL
349
350 if (ExtOverrides.empty() == false)
351 Over.ReadExtraOverride(ExtOverrides);
352
b2e465d6
AL
353 _error->DumpErrors();
354}
98953965
AL
355 /*}}}*/
356// FTWScanner::SetExts - Set extensions to support /*{{{*/
357// ---------------------------------------------------------------------
358/* */
9209ec47 359bool FTWScanner::SetExts(string const &Vals)
98953965
AL
360{
361 ClearPatterns();
362 string::size_type Start = 0;
d6689735 363 while (Start <= Vals.length()-1)
98953965 364 {
31981076
DK
365 string::size_type const Space = Vals.find(' ',Start);
366 string::size_type const Length = ((Space == string::npos) ? Vals.length() : Space) - Start;
367 if ( Arch.empty() == false )
98953965 368 {
31981076
DK
369 AddPattern(string("*_") + Arch + Vals.substr(Start, Length));
370 AddPattern(string("*_all") + Vals.substr(Start, Length));
98953965 371 }
d6689735 372 else
31981076
DK
373 AddPattern(string("*") + Vals.substr(Start, Length));
374
d6689735 375 Start += Length + 1;
98953965
AL
376 }
377
378 return true;
379}
380
b2e465d6
AL
381 /*}}}*/
382// PackagesWriter::DoPackage - Process a single package /*{{{*/
383// ---------------------------------------------------------------------
384/* This method takes a package and gets its control information and
cde41ae8
MV
385 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
386 rewritten and the path/size/hash appended. */
b2e465d6
AL
387bool PackagesWriter::DoPackage(string FileName)
388{
b2e465d6 389 // Pull all the data we need form the DB
a311fb96
DK
390 if (Db.GetFileInfo(FileName,
391 true, /* DoControl */
392 DoContents,
393 true, /* GenContentsOnly */
394 false, /* DoSource */
395 DoHashes, DoAlwaysStat) == false)
cde41ae8 396 {
ce928105 397 return false;
cde41ae8 398 }
b2e465d6 399
650faab0 400 unsigned long long FileSize = Db.GetFileSize();
cde41ae8 401 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
b2e465d6
AL
402 return false;
403
404 // Lookup the overide information
405 pkgTagSection &Tags = Db.Control.Section;
406 string Package = Tags.FindS("Package");
0b41e0e7
MV
407 string Architecture;
408 // if we generate a Packages file for a given arch, we use it to
409 // look for overrides. if we run in "simple" mode without the
410 // "Architecures" variable in the config we use the architecure value
411 // from the deb file
412 if(Arch != "")
413 Architecture = Arch;
414 else
415 Architecture = Tags.FindS("Architecture");
416 auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
b2e465d6
AL
417
418 if (Package.empty() == true)
dc738e7a 419 return _error->Error(_("Archive had no package field"));
0b41e0e7 420
b2e465d6 421 // If we need to do any rewriting of the header do it now..
0b41e0e7 422 if (OverItem.get() == 0)
b2e465d6
AL
423 {
424 if (NoOverride == false)
425 {
426 NewLine(1);
dc738e7a 427 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
b2e465d6
AL
428 }
429
0b41e0e7
MV
430 OverItem = auto_ptr<Override::Item>(new Override::Item);
431 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
432 OverItem->Priority = Tags.FindS("Priority");
b2e465d6
AL
433 }
434
b2e465d6
AL
435 // Strip the DirStrip prefix from the FileName and add the PathPrefix
436 string NewFileName;
437 if (DirStrip.empty() == false &&
438 FileName.length() > DirStrip.length() &&
439 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
440 DirStrip.begin(),DirStrip.end()) == 0)
441 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
442 else
443 NewFileName = FileName;
444 if (PathPrefix.empty() == false)
445 NewFileName = flCombine(PathPrefix,NewFileName);
9c24493f
DK
446
447 /* Configuration says we don't want to include the long Description
448 in the package file - instead we want to ship a separated file */
449 string desc;
450 if (LongDescription == false) {
451 desc = Tags.FindS("Description").append("\n");
452 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
453 }
454
b2e465d6 455 // This lists all the changes to the fields we are going to make.
88593886 456 std::vector<pkgTagSection::Tag> Changes;
64177f17 457
b8eba208
DK
458 std::string Size;
459 strprintf(Size, "%llu", (unsigned long long) FileSize);
88593886 460 Changes.push_back(pkgTagSection::Tag::Rewrite("Size", Size));
b8eba208 461
a311fb96
DK
462 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
463 {
464 if (hs->HashType() == "MD5Sum")
88593886 465 Changes.push_back(pkgTagSection::Tag::Rewrite("MD5sum", hs->HashValue()));
23397c9d
DK
466 else if (hs->HashType() == "Checksum-FileSize")
467 continue;
a311fb96 468 else
88593886 469 Changes.push_back(pkgTagSection::Tag::Rewrite(hs->HashType(), hs->HashValue()));
a311fb96 470 }
88593886
DK
471 Changes.push_back(pkgTagSection::Tag::Rewrite("Filename", NewFileName));
472 Changes.push_back(pkgTagSection::Tag::Rewrite("Priority", OverItem->Priority));
473 Changes.push_back(pkgTagSection::Tag::Remove("Status"));
474 Changes.push_back(pkgTagSection::Tag::Remove("Optional"));
64177f17 475
9c24493f
DK
476 string DescriptionMd5;
477 if (LongDescription == false) {
478 MD5Summation descmd5;
479 descmd5.Add(desc.c_str());
480 DescriptionMd5 = descmd5.Result().Value();
88593886 481 Changes.push_back(pkgTagSection::Tag::Rewrite("Description-md5", DescriptionMd5));
66905344
DK
482 if (TransWriter != NULL)
483 TransWriter->DoPackage(Package, desc, DescriptionMd5);
9c24493f
DK
484 }
485
b2e465d6
AL
486 // Rewrite the maintainer field if necessary
487 bool MaintFailed;
488 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
489 if (MaintFailed == true)
490 {
491 if (NoOverride == false)
492 {
493 NewLine(1);
dc738e7a
AL
494 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
495 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
eb0d90f0 496 }
b2e465d6 497 }
eb0d90f0 498
b2e465d6 499 if (NewMaint.empty() == false)
88593886 500 Changes.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint));
eb0d90f0 501
b2e465d6 502 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
c6474fb6 503 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
b2e465d6
AL
504 but dpkg does this append bit. So we do the append bit, at least that way the
505 status file and package file will remain similar. There are other transforms
506 but optional is the only legacy one still in use for some lazy reason. */
507 string OptionalStr = Tags.FindS("Optional");
508 if (OptionalStr.empty() == false)
509 {
510 if (Tags.FindS("Suggests").empty() == false)
511 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
88593886 512 Changes.push_back(pkgTagSection::Tag::Rewrite("Suggests", OptionalStr));
b2e465d6 513 }
64177f17 514
eb0d90f0 515 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
f7f0d6c7 516 I != OverItem->FieldOverride.end(); ++I)
88593886 517 Changes.push_back(pkgTagSection::Tag::Rewrite(I->first, I->second));
64177f17 518
b2e465d6 519 // Rewrite and store the fields.
88593886
DK
520 if (Tags.Write(*Output, TFRewritePackageOrder, Changes) == false ||
521 Output->Write("\n", 1) == false)
b2e465d6 522 return false;
b2e465d6
AL
523
524 return Db.Finish();
525}
526 /*}}}*/
527
66905344
DK
528// TranslationWriter::TranslationWriter - Constructor /*{{{*/
529// ---------------------------------------------------------------------
530/* Create a Translation-Master file for this Packages file */
34f1d96c 531TranslationWriter::TranslationWriter(string const &File, string const &TransCompress,
88593886 532 mode_t const &Permissions) : RefCounter(0)
66905344
DK
533{
534 if (File.empty() == true)
535 return;
536
34f1d96c 537 Comp = new MultiCompress(File, TransCompress, Permissions);
88593886 538 Output = &Comp->Input;
66905344
DK
539}
540 /*}}}*/
541// TranslationWriter::DoPackage - Process a single package /*{{{*/
542// ---------------------------------------------------------------------
543/* Create a Translation-Master file for this Packages file */
544bool TranslationWriter::DoPackage(string const &Pkg, string const &Desc,
545 string const &MD5)
546{
547 if (Output == NULL)
548 return true;
549
550 // Different archs can include different versions and therefore
551 // different descriptions - so we need to check for both name and md5.
552 string const Record = Pkg + ":" + MD5;
553
554 if (Included.find(Record) != Included.end())
555 return true;
556
88593886
DK
557 std::string out;
558 strprintf(out, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
66905344 559 Pkg.c_str(), MD5.c_str(), Desc.c_str());
88593886 560 Output->Write(out.c_str(), out.length());
66905344
DK
561
562 Included.insert(Record);
563 return true;
564}
565 /*}}}*/
566// TranslationWriter::~TranslationWriter - Destructor /*{{{*/
567// ---------------------------------------------------------------------
568/* */
569TranslationWriter::~TranslationWriter()
570{
34f1d96c
DK
571 if (Comp == NULL)
572 return;
573
574 delete Comp;
66905344
DK
575}
576 /*}}}*/
577
b2e465d6
AL
578// SourcesWriter::SourcesWriter - Constructor /*{{{*/
579// ---------------------------------------------------------------------
580/* */
88593886 581SourcesWriter::SourcesWriter(FileFd * const GivenOutput, string const &DB, string const &BOverrides,string const &SOverrides,
f6f06a8f 582 string const &ExtOverrides) :
88593886 583 FTWScanner(GivenOutput), Db(DB), Stats(Db.Stats)
b2e465d6 584{
98953965 585 AddPattern("*.dsc");
b2e465d6
AL
586 DeLinkLimit = 0;
587 Buffer = 0;
588 BufSize = 0;
589
590 // Process the command line options
a311fb96 591 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Sources");
b2e465d6 592 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
f6f06a8f 593 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
594
595 // Read the override file
596 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
597 return;
598 else
599 NoOverride = true;
64177f17 600
cde41ae8
MV
601 // WTF?? The logic above: if we can't read binary overrides, don't even try
602 // reading source overrides. if we can read binary overrides, then say there
603 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
604
64177f17
AL
605 if (ExtOverrides.empty() == false)
606 SOver.ReadExtraOverride(ExtOverrides);
b2e465d6 607
64177f17
AL
608 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
609 SOver.ReadOverride(SOverrides,true);
b2e465d6
AL
610}
611 /*}}}*/
612// SourcesWriter::DoPackage - Process a single package /*{{{*/
88593886 613static std::string getDscHash(unsigned int const DoHashes,
a311fb96
DK
614 Hashes::SupportedHashes const DoIt, pkgTagSection &Tags, char const * const FieldName,
615 HashString const * const Hash, unsigned long long Size, std::string FileName)
616{
617 if ((DoHashes & DoIt) != DoIt || Tags.Exists(FieldName) == false || Hash == NULL)
88593886
DK
618 return "";
619 std::ostringstream out;
a311fb96
DK
620 out << "\n " << Hash->HashValue() << " " << Size << " " << FileName
621 << "\n " << Tags.FindS(FieldName);
88593886 622 return out.str();
a311fb96 623}
b2e465d6 624bool SourcesWriter::DoPackage(string FileName)
f1828b69 625{
ce928105
MV
626 // Pull all the data we need form the DB
627 if (Db.GetFileInfo(FileName,
a311fb96
DK
628 false, /* DoControl */
629 false, /* DoContents */
630 false, /* GenContentsOnly */
631 true, /* DoSource */
632 DoHashes, DoAlwaysStat) == false)
b2e465d6 633 {
b2e465d6 634 return false;
ce928105 635 }
b2e465d6 636
ce928105
MV
637 // we need to perform a "write" here (this is what finish is doing)
638 // because the call to Db.GetFileInfo() in the loop will change
639 // the "db cursor"
640 Db.Finish();
f1828b69 641
f1828b69 642 pkgTagSection Tags;
31be38d2 643 if (Tags.Scan(Db.Dsc.Data.c_str(), Db.Dsc.Data.length()) == false)
f1828b69 644 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
31be38d2 645
ce928105
MV
646 if (Tags.Exists("Source") == false)
647 return _error->Error("Could not find a Source entry in the DSC '%s'",FileName.c_str());
b2e465d6 648 Tags.Trim();
f1828b69 649
b2e465d6
AL
650 // Lookup the overide information, finding first the best priority.
651 string BestPrio;
b2e465d6 652 string Bins = Tags.FindS("Binary");
5200ec6f 653 char Buffer[Bins.length() + 1];
0b41e0e7 654 auto_ptr<Override::Item> OverItem(0);
5200ec6f 655 if (Bins.empty() == false)
b2e465d6
AL
656 {
657 strcpy(Buffer,Bins.c_str());
658
659 // Ignore too-long errors.
660 char *BinList[400];
661 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
662
663 // Look at all the binaries
664 unsigned char BestPrioV = pkgCache::State::Extra;
665 for (unsigned I = 0; BinList[I] != 0; I++)
666 {
0b41e0e7
MV
667 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
668 if (Itm.get() == 0)
b2e465d6 669 continue;
b2e465d6
AL
670
671 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
672 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
673 {
674 BestPrioV = NewPrioV;
675 BestPrio = Itm->Priority;
676 }
7524e348
MV
677
678 if (OverItem.get() == 0)
679 OverItem = Itm;
b2e465d6
AL
680 }
681 }
682
683 // If we need to do any rewriting of the header do it now..
0b41e0e7 684 if (OverItem.get() == 0)
b2e465d6
AL
685 {
686 if (NoOverride == false)
687 {
688 NewLine(1);
dc738e7a 689 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
b2e465d6
AL
690 }
691
0b41e0e7 692 OverItem = auto_ptr<Override::Item>(new Override::Item);
b2e465d6
AL
693 }
694
ce928105
MV
695 struct stat St;
696 if (stat(FileName.c_str(), &St) != 0)
697 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
698
0b41e0e7 699 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
cde41ae8 700 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
0b41e0e7 701 if (SOverItem.get() == 0)
b2e465d6 702 {
cde41ae8 703 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
704 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
705 if (SOverItem.get() == 0)
706 {
cde41ae8 707 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
708 SOverItem = auto_ptr<Override::Item>(new Override::Item);
709 *SOverItem = *OverItem;
710 }
b2e465d6 711 }
a311fb96 712
b2e465d6 713 // Add the dsc to the files hash list
f99da908 714 string const strippedName = flNotDir(FileName);
88593886
DK
715 std::string const Files = getDscHash(DoHashes, Hashes::MD5SUM, Tags, "Files", Db.HashesList.find("MD5Sum"), St.st_size, strippedName);
716 std::string ChecksumsSha1 = getDscHash(DoHashes, Hashes::SHA1SUM, Tags, "Checksums-Sha1", Db.HashesList.find("SHA1"), St.st_size, strippedName);
717 std::string ChecksumsSha256 = getDscHash(DoHashes, Hashes::SHA256SUM, Tags, "Checksums-Sha256", Db.HashesList.find("SHA256"), St.st_size, strippedName);
718 std::string ChecksumsSha512 = getDscHash(DoHashes, Hashes::SHA512SUM, Tags, "Checksums-Sha512", Db.HashesList.find("SHA512"), St.st_size, strippedName);
9a961efc 719
b2e465d6
AL
720 // Strip the DirStrip prefix from the FileName and add the PathPrefix
721 string NewFileName;
722 if (DirStrip.empty() == false &&
723 FileName.length() > DirStrip.length() &&
8c58f506 724 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
b2e465d6
AL
725 NewFileName = string(OriginalPath + DirStrip.length());
726 else
727 NewFileName = OriginalPath;
728 if (PathPrefix.empty() == false)
729 NewFileName = flCombine(PathPrefix,NewFileName);
171c45bc 730
b2e465d6
AL
731 string Directory = flNotFile(OriginalPath);
732 string Package = Tags.FindS("Source");
171c45bc 733
f6f06a8f 734 // Perform operation over all of the files
b2e465d6 735 string ParseJnk;
bf99a6d3 736 const char *C = Files.c_str();
ab3846c0 737 char *RealPath = NULL;
b2e465d6
AL
738 for (;isspace(*C); C++);
739 while (*C != 0)
88593886 740 {
b2e465d6
AL
741 // Parse each of the elements
742 if (ParseQuoteWord(C,ParseJnk) == false ||
743 ParseQuoteWord(C,ParseJnk) == false ||
744 ParseQuoteWord(C,ParseJnk) == false)
745 return _error->Error("Error parsing file record");
f6f06a8f 746
b2e465d6 747 string OriginalPath = Directory + ParseJnk;
f6f06a8f
MV
748
749 // Add missing hashes to source files
a311fb96
DK
750 if (((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM && !Tags.Exists("Checksums-Sha1")) ||
751 ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM && !Tags.Exists("Checksums-Sha256")) ||
752 ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM && !Tags.Exists("Checksums-Sha512")))
f6f06a8f 753 {
a311fb96 754 if (Db.GetFileInfo(OriginalPath,
ce928105
MV
755 false, /* DoControl */
756 false, /* DoContents */
757 false, /* GenContentsOnly */
758 false, /* DoSource */
a311fb96 759 DoHashes,
ce928105 760 DoAlwaysStat) == false)
f6f06a8f
MV
761 {
762 return _error->Error("Error getting file info");
763 }
764
a311fb96
DK
765 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
766 {
23397c9d 767 if (hs->HashType() == "MD5Sum" || hs->HashType() == "Checksum-FileSize")
a311fb96
DK
768 continue;
769 char const * fieldname;
88593886 770 std::string * out;
a311fb96
DK
771 if (hs->HashType() == "SHA1")
772 {
773 fieldname = "Checksums-Sha1";
88593886 774 out = &ChecksumsSha1;
a311fb96
DK
775 }
776 else if (hs->HashType() == "SHA256")
777 {
778 fieldname = "Checksums-Sha256";
88593886 779 out = &ChecksumsSha256;
a311fb96
DK
780 }
781 else if (hs->HashType() == "SHA512")
782 {
783 fieldname = "Checksums-Sha512";
88593886 784 out = &ChecksumsSha512;
a311fb96
DK
785 }
786 else
787 {
788 _error->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs->HashType().c_str());
789 continue;
790 }
791 if (Tags.Exists(fieldname) == true)
792 continue;
88593886
DK
793 std::ostringstream streamout;
794 streamout << "\n " << hs->HashValue() << " " << Db.GetFileSize() << " " << ParseJnk;
795 out->append(streamout.str());
a311fb96 796 }
ce928105 797
88593886 798 // write back the GetFileInfo() stats data
a311fb96 799 Db.Finish();
f6f06a8f
MV
800 }
801
802 // Perform the delinking operation
803 char Jnk[2];
804
ab3846c0
MV
805 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
806 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
b2e465d6
AL
807 {
808 string RP = RealPath;
ab3846c0 809 free(RealPath);
cde41ae8 810 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
b2e465d6
AL
811 return false;
812 }
813 }
814
815 Directory = flNotFile(NewFileName);
816 if (Directory.length() > 2)
817 Directory.erase(Directory.end()-1);
171c45bc 818
b2e465d6 819 // This lists all the changes to the fields we are going to make.
f99da908 820 // (5 hardcoded + checksums + maintainer + end marker)
88593886 821 std::vector<pkgTagSection::Tag> Changes;
64177f17 822
88593886
DK
823 Changes.push_back(pkgTagSection::Tag::Remove("Source"));
824 Changes.push_back(pkgTagSection::Tag::Rewrite("Package", Package));
3c54407f 825 if (Files.empty() == false)
88593886 826 Changes.push_back(pkgTagSection::Tag::Rewrite("Files", Files));
3c54407f 827 if (ChecksumsSha1.empty() == false)
88593886 828 Changes.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha1", ChecksumsSha1));
3c54407f 829 if (ChecksumsSha256.empty() == false)
88593886 830 Changes.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha256", ChecksumsSha256));
12cd178d 831 if (ChecksumsSha512.empty() == false)
88593886 832 Changes.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha512", ChecksumsSha512));
171c45bc 833 if (Directory != "./")
88593886
DK
834 Changes.push_back(pkgTagSection::Tag::Rewrite("Directory", Directory));
835 Changes.push_back(pkgTagSection::Tag::Rewrite("Priority", BestPrio));
836 Changes.push_back(pkgTagSection::Tag::Remove("Status"));
b2e465d6
AL
837
838 // Rewrite the maintainer field if necessary
839 bool MaintFailed;
88593886 840 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"), MaintFailed);
b2e465d6
AL
841 if (MaintFailed == true)
842 {
843 if (NoOverride == false)
844 {
88593886 845 NewLine(1);
dc738e7a
AL
846 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
847 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
88593886 848 }
b2e465d6
AL
849 }
850 if (NewMaint.empty() == false)
88593886
DK
851 Changes.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint.c_str()));
852
853 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
f7f0d6c7 854 I != SOverItem->FieldOverride.end(); ++I)
88593886 855 Changes.push_back(pkgTagSection::Tag::Rewrite(I->first, I->second));
64177f17 856
b2e465d6 857 // Rewrite and store the fields.
88593886
DK
858 if (Tags.Write(*Output, TFRewriteSourceOrder, Changes) == false ||
859 Output->Write("\n", 1) == false)
b2e465d6 860 return false;
b2e465d6
AL
861
862 Stats.Packages++;
863
ce928105 864 return true;
b2e465d6
AL
865}
866 /*}}}*/
867
868// ContentsWriter::ContentsWriter - Constructor /*{{{*/
869// ---------------------------------------------------------------------
870/* */
88593886
DK
871ContentsWriter::ContentsWriter(FileFd * const GivenOutput, string const &DB, string const &Arch) :
872 FTWScanner(GivenOutput, Arch), Db(DB), Stats(Db.Stats)
b2e465d6
AL
873
874{
31981076 875 SetExts(".deb");
b2e465d6
AL
876}
877 /*}}}*/
878// ContentsWriter::DoPackage - Process a single package /*{{{*/
879// ---------------------------------------------------------------------
880/* If Package is the empty string the control record will be parsed to
881 determine what the package name is. */
9209ec47 882bool ContentsWriter::DoPackage(string FileName, string Package)
b2e465d6 883{
a311fb96
DK
884 if (!Db.GetFileInfo(FileName,
885 Package.empty(), /* DoControl */
886 true, /* DoContents */
887 false, /* GenContentsOnly */
888 false, /* DoSource */
889 0, /* DoHashes */
890 false /* checkMtime */))
cde41ae8 891 {
b2e465d6 892 return false;
cde41ae8 893 }
b2e465d6
AL
894
895 // Parse the package name
896 if (Package.empty() == true)
897 {
b2e465d6
AL
898 Package = Db.Control.Section.FindS("Package");
899 }
900
901 Db.Contents.Add(Gen,Package);
902
903 return Db.Finish();
904}
905 /*}}}*/
906// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
907// ---------------------------------------------------------------------
908/* */
9209ec47 909bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
b2e465d6
AL
910{
911 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
912 if (_error->PendingError() == true)
913 return false;
12d1f5b3 914
b2e465d6 915 // Open the package file
12d1f5b3
DK
916 FileFd Fd;
917 if (Pkgs.OpenOld(Fd) == false)
b2e465d6 918 return false;
12d1f5b3 919
b2e465d6
AL
920 pkgTagFile Tags(&Fd);
921 if (_error->PendingError() == true)
b2e465d6 922 return false;
12d1f5b3 923
b2e465d6
AL
924 // Parse.
925 pkgTagSection Section;
926 while (Tags.Step(Section) == true)
927 {
928 string File = flCombine(Prefix,Section.FindS("FileName"));
929 string Package = Section.FindS("Section");
930 if (Package.empty() == false && Package.end()[-1] != '/')
931 {
932 Package += '/';
933 Package += Section.FindS("Package");
934 }
935 else
936 Package += Section.FindS("Package");
937
938 DoPackage(File,Package);
939 if (_error->empty() == false)
940 {
941 _error->Error("Errors apply to file '%s'",File.c_str());
942 _error->DumpErrors();
943 }
944 }
12d1f5b3 945
b2e465d6 946 // Tidy the compressor
12d1f5b3
DK
947 Fd.Close();
948
b2e465d6
AL
949 return true;
950}
98953965 951
b2e465d6 952 /*}}}*/
98953965
AL
953
954// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
955// ---------------------------------------------------------------------
956/* */
88593886 957ReleaseWriter::ReleaseWriter(FileFd * const GivenOutput, string const &/*DB*/) : FTWScanner(GivenOutput)
98953965 958{
3cb3fe76
DK
959 if (_config->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
960 {
961 AddPattern("Packages");
962 AddPattern("Packages.gz");
963 AddPattern("Packages.bz2");
964 AddPattern("Packages.lzma");
b7080ced 965 AddPattern("Packages.xz");
8e3900d0 966 AddPattern("Translation-*");
3cb3fe76
DK
967 AddPattern("Sources");
968 AddPattern("Sources.gz");
969 AddPattern("Sources.bz2");
970 AddPattern("Sources.lzma");
b7080ced 971 AddPattern("Sources.xz");
3cb3fe76 972 AddPattern("Release");
0baf849d 973 AddPattern("Contents-*");
8d16c617 974 AddPattern("Index");
3cb3fe76
DK
975 AddPattern("md5sum.txt");
976 }
977 AddPatterns(_config->FindVector("APT::FTPArchive::Release::Patterns"));
187492a6 978
9209ec47 979 time_t const now = time(NULL);
cb12d0a6
DK
980
981 setlocale(LC_TIME, "C");
982
98953965
AL
983 char datestr[128];
984 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
985 gmtime(&now)) == 0)
986 {
987 datestr[0] = '\0';
988 }
989
c99e48ec 990 time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
cdb623ed 991 char validstr[128];
c99e48ec
DK
992 if (now == validuntil ||
993 strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
994 gmtime(&validuntil)) == 0)
995 {
cdb623ed 996 validstr[0] = '\0';
c99e48ec
DK
997 }
998
cb12d0a6
DK
999 setlocale(LC_TIME, "");
1000
98953965
AL
1001 map<string,string> Fields;
1002 Fields["Origin"] = "";
1003 Fields["Label"] = "";
1004 Fields["Suite"] = "";
1005 Fields["Version"] = "";
1006 Fields["Codename"] = "";
1007 Fields["Date"] = datestr;
c99e48ec 1008 Fields["Valid-Until"] = validstr;
98953965
AL
1009 Fields["Architectures"] = "";
1010 Fields["Components"] = "";
1011 Fields["Description"] = "";
1012
1013 for(map<string,string>::const_iterator I = Fields.begin();
1014 I != Fields.end();
1015 ++I)
1016 {
1017 string Config = string("APT::FTPArchive::Release::") + (*I).first;
1018 string Value = _config->Find(Config, (*I).second.c_str());
1019 if (Value == "")
1020 continue;
1021
88593886
DK
1022 std::string const out = I->first + ": " + Value + "\n";
1023 Output->Write(out.c_str(), out.length());
98953965 1024 }
3c54407f 1025
a311fb96 1026 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Release");
98953965
AL
1027}
1028 /*}}}*/
1029// ReleaseWriter::DoPackage - Process a single package /*{{{*/
1030// ---------------------------------------------------------------------
1031bool ReleaseWriter::DoPackage(string FileName)
1032{
1033 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1034 string NewFileName;
1035 if (DirStrip.empty() == false &&
1036 FileName.length() > DirStrip.length() &&
1037 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
1038 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 1039 {
98953965 1040 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 1041 while (NewFileName[0] == '/')
9202409d 1042 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 1043 }
98953965
AL
1044 else
1045 NewFileName = FileName;
c0eb6bc6 1046
98953965
AL
1047 if (PathPrefix.empty() == false)
1048 NewFileName = flCombine(PathPrefix,NewFileName);
1049
1050 FileFd fd(FileName, FileFd::ReadOnly);
1051
1052 if (!fd.IsOpen())
1053 {
1054 return false;
1055 }
1056
c0eb6bc6 1057 CheckSums[NewFileName].size = fd.Size();
f7291f62 1058
9224ce3d
DK
1059 Hashes hs(DoHashes);
1060 hs.AddFD(fd);
a311fb96 1061 CheckSums[NewFileName].Hashes = hs.GetHashStringList();
98953965 1062 fd.Close();
8c4e1f97 1063
98953965
AL
1064 return true;
1065}
f7291f62
AL
1066
1067 /*}}}*/
1068// ReleaseWriter::Finish - Output the checksums /*{{{*/
1069// ---------------------------------------------------------------------
88593886 1070static void printChecksumTypeRecord(FileFd &Output, char const * const Type, map<string, ReleaseWriter::CheckSum> const &CheckSums)
f7291f62 1071{
88593886
DK
1072 {
1073 std::string out;
1074 strprintf(out, "%s:\n", Type);
1075 Output.Write(out.c_str(), out.length());
1076 }
1077 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
1078 I != CheckSums.end(); ++I)
1079 {
1080 HashString const * const hs = I->second.Hashes.find(Type);
1081 if (hs == NULL)
1082 continue;
1083 std::string out;
1084 strprintf(out, " %s %16llu %s\n",
1085 hs->HashValue().c_str(),
1086 (*I).second.size,
1087 (*I).first.c_str());
1088 Output.Write(out.c_str(), out.length());
1089 }
a311fb96
DK
1090}
1091void ReleaseWriter::Finish()
1092{
1093 if ((DoHashes & Hashes::MD5SUM) == Hashes::MD5SUM)
88593886 1094 printChecksumTypeRecord(*Output, "MD5Sum", CheckSums);
a311fb96 1095 if ((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM)
88593886 1096 printChecksumTypeRecord(*Output, "SHA1", CheckSums);
a311fb96 1097 if ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM)
88593886 1098 printChecksumTypeRecord(*Output, "SHA256", CheckSums);
a311fb96 1099 if ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM)
88593886 1100 printChecksumTypeRecord(*Output, "SHA512", CheckSums);
f7291f62 1101}