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