]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
add pkgAcquire::TransactionHasError()
[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
64177f17
AL
54// SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
55// ---------------------------------------------------------------------
56/* */
eb0d90f0 57static inline TFRewriteData SetTFRewriteData(const char *tag,
64177f17
AL
58 const char *rewrite,
59 const char *newtag = 0)
60{
eb0d90f0
DK
61 TFRewriteData tfrd;
62 tfrd.Tag = tag;
63 tfrd.Rewrite = rewrite;
64 tfrd.NewTag = newtag;
65 return tfrd;
64177f17
AL
66}
67 /*}}}*/
a311fb96
DK
68// ConfigToDoHashes - which hashes to generate /*{{{*/
69static void SingleConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf, unsigned int const Flag)
70{
71 if (_config->FindB(Conf, true) == true)
72 DoHashes |= Flag;
73 else
74 DoHashes &= ~Flag;
75}
76static void ConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf)
77{
78 SingleConfigToDoHashes(DoHashes, Conf + "::MD5", Hashes::MD5SUM);
79 SingleConfigToDoHashes(DoHashes, Conf + "::SHA1", Hashes::SHA1SUM);
80 SingleConfigToDoHashes(DoHashes, Conf + "::SHA256", Hashes::SHA256SUM);
81 SingleConfigToDoHashes(DoHashes, Conf + "::SHA512", Hashes::SHA512SUM);
82}
83 /*}}}*/
64177f17 84
b2e465d6
AL
85// FTWScanner::FTWScanner - Constructor /*{{{*/
86// ---------------------------------------------------------------------
87/* */
a311fb96 88FTWScanner::FTWScanner(string const &Arch): Arch(Arch), DoHashes(~0)
b2e465d6
AL
89{
90 ErrorPrinted = false;
91 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
a311fb96 92 ConfigToDoHashes(DoHashes, "APT::FTPArchive");
b2e465d6
AL
93}
94 /*}}}*/
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/* */
9209ec47 334PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string const &ExtOverrides,
31981076 335 string const &Arch) :
66905344 336 FTWScanner(Arch), Db(DB), Stats(Db.Stats), TransWriter(NULL)
b2e465d6
AL
337{
338 Output = stdout;
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}
388
b2e465d6
AL
389 /*}}}*/
390// PackagesWriter::DoPackage - Process a single package /*{{{*/
391// ---------------------------------------------------------------------
392/* This method takes a package and gets its control information and
cde41ae8
MV
393 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
394 rewritten and the path/size/hash appended. */
b2e465d6
AL
395bool PackagesWriter::DoPackage(string FileName)
396{
b2e465d6 397 // Pull all the data we need form the DB
a311fb96
DK
398 if (Db.GetFileInfo(FileName,
399 true, /* DoControl */
400 DoContents,
401 true, /* GenContentsOnly */
402 false, /* DoSource */
403 DoHashes, DoAlwaysStat) == false)
cde41ae8 404 {
ce928105 405 return false;
cde41ae8 406 }
b2e465d6 407
650faab0 408 unsigned long long FileSize = Db.GetFileSize();
cde41ae8 409 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
b2e465d6
AL
410 return false;
411
412 // Lookup the overide information
413 pkgTagSection &Tags = Db.Control.Section;
414 string Package = Tags.FindS("Package");
0b41e0e7
MV
415 string Architecture;
416 // if we generate a Packages file for a given arch, we use it to
417 // look for overrides. if we run in "simple" mode without the
418 // "Architecures" variable in the config we use the architecure value
419 // from the deb file
420 if(Arch != "")
421 Architecture = Arch;
422 else
423 Architecture = Tags.FindS("Architecture");
424 auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
b2e465d6
AL
425
426 if (Package.empty() == true)
dc738e7a 427 return _error->Error(_("Archive had no package field"));
0b41e0e7 428
b2e465d6 429 // If we need to do any rewriting of the header do it now..
0b41e0e7 430 if (OverItem.get() == 0)
b2e465d6
AL
431 {
432 if (NoOverride == false)
433 {
434 NewLine(1);
dc738e7a 435 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
b2e465d6
AL
436 }
437
0b41e0e7
MV
438 OverItem = auto_ptr<Override::Item>(new Override::Item);
439 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
440 OverItem->Priority = Tags.FindS("Priority");
b2e465d6
AL
441 }
442
443 char Size[40];
650faab0 444 sprintf(Size,"%llu", (unsigned long long) FileSize);
b2e465d6
AL
445
446 // Strip the DirStrip prefix from the FileName and add the PathPrefix
447 string NewFileName;
448 if (DirStrip.empty() == false &&
449 FileName.length() > DirStrip.length() &&
450 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
451 DirStrip.begin(),DirStrip.end()) == 0)
452 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
453 else
454 NewFileName = FileName;
455 if (PathPrefix.empty() == false)
456 NewFileName = flCombine(PathPrefix,NewFileName);
9c24493f
DK
457
458 /* Configuration says we don't want to include the long Description
459 in the package file - instead we want to ship a separated file */
460 string desc;
461 if (LongDescription == false) {
462 desc = Tags.FindS("Description").append("\n");
463 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
464 }
465
b2e465d6 466 // This lists all the changes to the fields we are going to make.
eb0d90f0 467 std::vector<TFRewriteData> Changes;
64177f17 468
eb0d90f0 469 Changes.push_back(SetTFRewriteData("Size", Size));
a311fb96
DK
470 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
471 {
472 if (hs->HashType() == "MD5Sum")
473 Changes.push_back(SetTFRewriteData("MD5sum", hs->HashValue().c_str()));
474 else
475 Changes.push_back(SetTFRewriteData(hs->HashType().c_str(), hs->HashValue().c_str()));
476 }
eb0d90f0
DK
477 Changes.push_back(SetTFRewriteData("Filename", NewFileName.c_str()));
478 Changes.push_back(SetTFRewriteData("Priority", OverItem->Priority.c_str()));
479 Changes.push_back(SetTFRewriteData("Status", 0));
480 Changes.push_back(SetTFRewriteData("Optional", 0));
64177f17 481
9c24493f
DK
482 string DescriptionMd5;
483 if (LongDescription == false) {
484 MD5Summation descmd5;
485 descmd5.Add(desc.c_str());
486 DescriptionMd5 = descmd5.Result().Value();
eb0d90f0 487 Changes.push_back(SetTFRewriteData("Description-md5", DescriptionMd5.c_str()));
66905344
DK
488 if (TransWriter != NULL)
489 TransWriter->DoPackage(Package, desc, DescriptionMd5);
9c24493f
DK
490 }
491
b2e465d6
AL
492 // Rewrite the maintainer field if necessary
493 bool MaintFailed;
494 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
495 if (MaintFailed == true)
496 {
497 if (NoOverride == false)
498 {
499 NewLine(1);
dc738e7a
AL
500 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
501 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
eb0d90f0 502 }
b2e465d6 503 }
eb0d90f0 504
b2e465d6 505 if (NewMaint.empty() == false)
eb0d90f0
DK
506 Changes.push_back(SetTFRewriteData("Maintainer", NewMaint.c_str()));
507
b2e465d6 508 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
c6474fb6 509 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
b2e465d6
AL
510 but dpkg does this append bit. So we do the append bit, at least that way the
511 status file and package file will remain similar. There are other transforms
512 but optional is the only legacy one still in use for some lazy reason. */
513 string OptionalStr = Tags.FindS("Optional");
514 if (OptionalStr.empty() == false)
515 {
516 if (Tags.FindS("Suggests").empty() == false)
517 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
eb0d90f0 518 Changes.push_back(SetTFRewriteData("Suggests", OptionalStr.c_str()));
b2e465d6 519 }
64177f17 520
eb0d90f0 521 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
f7f0d6c7 522 I != OverItem->FieldOverride.end(); ++I)
eb0d90f0 523 Changes.push_back(SetTFRewriteData(I->first.c_str(),I->second.c_str()));
64177f17 524
eb0d90f0 525 Changes.push_back(SetTFRewriteData( 0, 0));
64177f17 526
b2e465d6 527 // Rewrite and store the fields.
eb0d90f0 528 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes.data()) == false)
b2e465d6
AL
529 return false;
530 fprintf(Output,"\n");
531
532 return Db.Finish();
533}
534 /*}}}*/
535
66905344
DK
536// TranslationWriter::TranslationWriter - Constructor /*{{{*/
537// ---------------------------------------------------------------------
538/* Create a Translation-Master file for this Packages file */
34f1d96c
DK
539TranslationWriter::TranslationWriter(string const &File, string const &TransCompress,
540 mode_t const &Permissions) : Output(NULL),
66905344
DK
541 RefCounter(0)
542{
543 if (File.empty() == true)
544 return;
545
34f1d96c
DK
546 Comp = new MultiCompress(File, TransCompress, Permissions);
547 Output = Comp->Input;
66905344
DK
548}
549 /*}}}*/
550// TranslationWriter::DoPackage - Process a single package /*{{{*/
551// ---------------------------------------------------------------------
552/* Create a Translation-Master file for this Packages file */
553bool TranslationWriter::DoPackage(string const &Pkg, string const &Desc,
554 string const &MD5)
555{
556 if (Output == NULL)
557 return true;
558
559 // Different archs can include different versions and therefore
560 // different descriptions - so we need to check for both name and md5.
561 string const Record = Pkg + ":" + MD5;
562
563 if (Included.find(Record) != Included.end())
564 return true;
565
566 fprintf(Output, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
567 Pkg.c_str(), MD5.c_str(), Desc.c_str());
568
569 Included.insert(Record);
570 return true;
571}
572 /*}}}*/
573// TranslationWriter::~TranslationWriter - Destructor /*{{{*/
574// ---------------------------------------------------------------------
575/* */
576TranslationWriter::~TranslationWriter()
577{
34f1d96c
DK
578 if (Comp == NULL)
579 return;
580
581 delete Comp;
66905344
DK
582}
583 /*}}}*/
584
b2e465d6
AL
585// SourcesWriter::SourcesWriter - Constructor /*{{{*/
586// ---------------------------------------------------------------------
587/* */
f6f06a8f
MV
588SourcesWriter::SourcesWriter(string const &DB, string const &BOverrides,string const &SOverrides,
589 string const &ExtOverrides) :
590 Db(DB), Stats(Db.Stats)
b2e465d6
AL
591{
592 Output = stdout;
98953965 593 AddPattern("*.dsc");
b2e465d6
AL
594 DeLinkLimit = 0;
595 Buffer = 0;
596 BufSize = 0;
597
598 // Process the command line options
a311fb96 599 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Sources");
b2e465d6 600 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
f6f06a8f 601 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
602
603 // Read the override file
604 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
605 return;
606 else
607 NoOverride = true;
64177f17 608
cde41ae8
MV
609 // WTF?? The logic above: if we can't read binary overrides, don't even try
610 // reading source overrides. if we can read binary overrides, then say there
611 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
612
64177f17
AL
613 if (ExtOverrides.empty() == false)
614 SOver.ReadExtraOverride(ExtOverrides);
b2e465d6 615
64177f17
AL
616 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
617 SOver.ReadOverride(SOverrides,true);
b2e465d6
AL
618}
619 /*}}}*/
620// SourcesWriter::DoPackage - Process a single package /*{{{*/
a311fb96
DK
621static std::ostream& addDscHash(std::ostream &out, unsigned int const DoHashes,
622 Hashes::SupportedHashes const DoIt, pkgTagSection &Tags, char const * const FieldName,
623 HashString const * const Hash, unsigned long long Size, std::string FileName)
624{
625 if ((DoHashes & DoIt) != DoIt || Tags.Exists(FieldName) == false || Hash == NULL)
626 return out;
627 out << "\n " << Hash->HashValue() << " " << Size << " " << FileName
628 << "\n " << Tags.FindS(FieldName);
629 return out;
630}
b2e465d6 631bool SourcesWriter::DoPackage(string FileName)
f1828b69 632{
ce928105
MV
633 // Pull all the data we need form the DB
634 if (Db.GetFileInfo(FileName,
a311fb96
DK
635 false, /* DoControl */
636 false, /* DoContents */
637 false, /* GenContentsOnly */
638 true, /* DoSource */
639 DoHashes, DoAlwaysStat) == false)
b2e465d6 640 {
b2e465d6 641 return false;
ce928105 642 }
b2e465d6 643
ce928105
MV
644 // we need to perform a "write" here (this is what finish is doing)
645 // because the call to Db.GetFileInfo() in the loop will change
646 // the "db cursor"
647 Db.Finish();
f1828b69 648
ce928105
MV
649 // read stuff
650 char *Start = Db.Dsc.Data;
651 char *BlkEnd = Db.Dsc.Data + Db.Dsc.Length;
f1828b69
DK
652
653 // Add extra \n to the end, just in case (as in clearsigned they are missing)
654 *BlkEnd++ = '\n';
655 *BlkEnd++ = '\n';
656
657 pkgTagSection Tags;
ce928105 658 if (Tags.Scan(Start,BlkEnd - Start) == false)
f1828b69 659 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
ce928105
MV
660
661 if (Tags.Exists("Source") == false)
662 return _error->Error("Could not find a Source entry in the DSC '%s'",FileName.c_str());
b2e465d6 663 Tags.Trim();
f1828b69 664
b2e465d6
AL
665 // Lookup the overide information, finding first the best priority.
666 string BestPrio;
b2e465d6 667 string Bins = Tags.FindS("Binary");
5200ec6f 668 char Buffer[Bins.length() + 1];
0b41e0e7 669 auto_ptr<Override::Item> OverItem(0);
5200ec6f 670 if (Bins.empty() == false)
b2e465d6
AL
671 {
672 strcpy(Buffer,Bins.c_str());
673
674 // Ignore too-long errors.
675 char *BinList[400];
676 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
677
678 // Look at all the binaries
679 unsigned char BestPrioV = pkgCache::State::Extra;
680 for (unsigned I = 0; BinList[I] != 0; I++)
681 {
0b41e0e7
MV
682 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
683 if (Itm.get() == 0)
b2e465d6 684 continue;
b2e465d6
AL
685
686 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
687 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
688 {
689 BestPrioV = NewPrioV;
690 BestPrio = Itm->Priority;
691 }
7524e348
MV
692
693 if (OverItem.get() == 0)
694 OverItem = Itm;
b2e465d6
AL
695 }
696 }
697
698 // If we need to do any rewriting of the header do it now..
0b41e0e7 699 if (OverItem.get() == 0)
b2e465d6
AL
700 {
701 if (NoOverride == false)
702 {
703 NewLine(1);
dc738e7a 704 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
b2e465d6
AL
705 }
706
0b41e0e7 707 OverItem = auto_ptr<Override::Item>(new Override::Item);
b2e465d6
AL
708 }
709
ce928105
MV
710 struct stat St;
711 if (stat(FileName.c_str(), &St) != 0)
712 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
713
0b41e0e7 714 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
cde41ae8 715 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
0b41e0e7 716 if (SOverItem.get() == 0)
b2e465d6 717 {
cde41ae8 718 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
719 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
720 if (SOverItem.get() == 0)
721 {
cde41ae8 722 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
723 SOverItem = auto_ptr<Override::Item>(new Override::Item);
724 *SOverItem = *OverItem;
725 }
b2e465d6 726 }
a311fb96 727
b2e465d6 728 // Add the dsc to the files hash list
f99da908 729 string const strippedName = flNotDir(FileName);
bf99a6d3 730 std::ostringstream ostreamFiles;
a311fb96 731 addDscHash(ostreamFiles, DoHashes, Hashes::MD5SUM, Tags, "Files", Db.HashesList.find("MD5Sum"), St.st_size, strippedName);
bf99a6d3
DK
732 string const Files = ostreamFiles.str();
733
734 std::ostringstream ostreamSha1;
a311fb96 735 addDscHash(ostreamSha1, DoHashes, Hashes::SHA1SUM, Tags, "Checksums-Sha1", Db.HashesList.find("SHA1"), St.st_size, strippedName);
bf99a6d3 736 std::ostringstream ostreamSha256;
a311fb96 737 addDscHash(ostreamSha256, DoHashes, Hashes::SHA256SUM, Tags, "Checksums-Sha256", Db.HashesList.find("SHA256"), St.st_size, strippedName);
9a961efc 738 std::ostringstream ostreamSha512;
a311fb96 739 addDscHash(ostreamSha512, DoHashes, Hashes::SHA512SUM, Tags, "Checksums-Sha512", Db.HashesList.find("SHA512"), St.st_size, strippedName);
9a961efc 740
b2e465d6
AL
741 // Strip the DirStrip prefix from the FileName and add the PathPrefix
742 string NewFileName;
743 if (DirStrip.empty() == false &&
744 FileName.length() > DirStrip.length() &&
8c58f506 745 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
b2e465d6
AL
746 NewFileName = string(OriginalPath + DirStrip.length());
747 else
748 NewFileName = OriginalPath;
749 if (PathPrefix.empty() == false)
750 NewFileName = flCombine(PathPrefix,NewFileName);
171c45bc 751
b2e465d6
AL
752 string Directory = flNotFile(OriginalPath);
753 string Package = Tags.FindS("Source");
171c45bc 754
f6f06a8f 755 // Perform operation over all of the files
b2e465d6 756 string ParseJnk;
bf99a6d3 757 const char *C = Files.c_str();
ab3846c0 758 char *RealPath = NULL;
b2e465d6
AL
759 for (;isspace(*C); C++);
760 while (*C != 0)
761 {
762 // Parse each of the elements
763 if (ParseQuoteWord(C,ParseJnk) == false ||
764 ParseQuoteWord(C,ParseJnk) == false ||
765 ParseQuoteWord(C,ParseJnk) == false)
766 return _error->Error("Error parsing file record");
f6f06a8f 767
b2e465d6 768 string OriginalPath = Directory + ParseJnk;
f6f06a8f
MV
769
770 // Add missing hashes to source files
a311fb96
DK
771 if (((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM && !Tags.Exists("Checksums-Sha1")) ||
772 ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM && !Tags.Exists("Checksums-Sha256")) ||
773 ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM && !Tags.Exists("Checksums-Sha512")))
f6f06a8f 774 {
a311fb96 775 if (Db.GetFileInfo(OriginalPath,
ce928105
MV
776 false, /* DoControl */
777 false, /* DoContents */
778 false, /* GenContentsOnly */
779 false, /* DoSource */
a311fb96 780 DoHashes,
ce928105 781 DoAlwaysStat) == false)
f6f06a8f
MV
782 {
783 return _error->Error("Error getting file info");
784 }
785
a311fb96
DK
786 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
787 {
788 if (hs->HashType() == "MD5Sum")
789 continue;
790 char const * fieldname;
791 std::ostream * out;
792 if (hs->HashType() == "SHA1")
793 {
794 fieldname = "Checksums-Sha1";
795 out = &ostreamSha1;
796 }
797 else if (hs->HashType() == "SHA256")
798 {
799 fieldname = "Checksums-Sha256";
800 out = &ostreamSha256;
801 }
802 else if (hs->HashType() == "SHA512")
803 {
804 fieldname = "Checksums-Sha512";
805 out = &ostreamSha512;
806 }
807 else
808 {
809 _error->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs->HashType().c_str());
810 continue;
811 }
812 if (Tags.Exists(fieldname) == true)
813 continue;
814 (*out) << "\n " << hs->HashValue() << " " << Db.GetFileSize() << " " << ParseJnk;
815 }
ce928105 816
a311fb96
DK
817 // write back the GetFileInfo() stats data
818 Db.Finish();
f6f06a8f
MV
819 }
820
821 // Perform the delinking operation
822 char Jnk[2];
823
ab3846c0
MV
824 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
825 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
b2e465d6
AL
826 {
827 string RP = RealPath;
ab3846c0 828 free(RealPath);
cde41ae8 829 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
b2e465d6
AL
830 return false;
831 }
832 }
833
834 Directory = flNotFile(NewFileName);
835 if (Directory.length() > 2)
836 Directory.erase(Directory.end()-1);
171c45bc 837
f6f06a8f
MV
838 string const ChecksumsSha1 = ostreamSha1.str();
839 string const ChecksumsSha256 = ostreamSha256.str();
840 string const ChecksumsSha512 = ostreamSha512.str();
841
b2e465d6 842 // This lists all the changes to the fields we are going to make.
f99da908 843 // (5 hardcoded + checksums + maintainer + end marker)
eb0d90f0 844 std::vector<TFRewriteData> Changes;
64177f17 845
eb0d90f0 846 Changes.push_back(SetTFRewriteData("Source",Package.c_str(),"Package"));
3c54407f 847 if (Files.empty() == false)
eb0d90f0 848 Changes.push_back(SetTFRewriteData("Files",Files.c_str()));
3c54407f 849 if (ChecksumsSha1.empty() == false)
eb0d90f0 850 Changes.push_back(SetTFRewriteData("Checksums-Sha1",ChecksumsSha1.c_str()));
3c54407f 851 if (ChecksumsSha256.empty() == false)
eb0d90f0 852 Changes.push_back(SetTFRewriteData("Checksums-Sha256",ChecksumsSha256.c_str()));
12cd178d 853 if (ChecksumsSha512.empty() == false)
eb0d90f0 854 Changes.push_back(SetTFRewriteData("Checksums-Sha512",ChecksumsSha512.c_str()));
171c45bc 855 if (Directory != "./")
eb0d90f0
DK
856 Changes.push_back(SetTFRewriteData("Directory",Directory.c_str()));
857 Changes.push_back(SetTFRewriteData("Priority",BestPrio.c_str()));
858 Changes.push_back(SetTFRewriteData("Status",0));
b2e465d6
AL
859
860 // Rewrite the maintainer field if necessary
861 bool MaintFailed;
862 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
863 if (MaintFailed == true)
864 {
865 if (NoOverride == false)
866 {
867 NewLine(1);
dc738e7a
AL
868 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
869 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
b2e465d6
AL
870 }
871 }
872 if (NewMaint.empty() == false)
eb0d90f0 873 Changes.push_back(SetTFRewriteData("Maintainer", NewMaint.c_str()));
64177f17 874
9209ec47 875 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
f7f0d6c7 876 I != SOverItem->FieldOverride.end(); ++I)
eb0d90f0 877 Changes.push_back(SetTFRewriteData(I->first.c_str(),I->second.c_str()));
64177f17 878
eb0d90f0 879 Changes.push_back(SetTFRewriteData(0, 0));
b2e465d6
AL
880
881 // Rewrite and store the fields.
eb0d90f0 882 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes.data()) == false)
b2e465d6
AL
883 return false;
884 fprintf(Output,"\n");
885
886 Stats.Packages++;
887
ce928105 888 return true;
b2e465d6
AL
889}
890 /*}}}*/
891
892// ContentsWriter::ContentsWriter - Constructor /*{{{*/
893// ---------------------------------------------------------------------
894/* */
31981076
DK
895ContentsWriter::ContentsWriter(string const &DB, string const &Arch) :
896 FTWScanner(Arch), Db(DB), Stats(Db.Stats)
b2e465d6
AL
897
898{
31981076 899 SetExts(".deb");
b2e465d6
AL
900 Output = stdout;
901}
902 /*}}}*/
903// ContentsWriter::DoPackage - Process a single package /*{{{*/
904// ---------------------------------------------------------------------
905/* If Package is the empty string the control record will be parsed to
906 determine what the package name is. */
9209ec47 907bool ContentsWriter::DoPackage(string FileName, string Package)
b2e465d6 908{
a311fb96
DK
909 if (!Db.GetFileInfo(FileName,
910 Package.empty(), /* DoControl */
911 true, /* DoContents */
912 false, /* GenContentsOnly */
913 false, /* DoSource */
914 0, /* DoHashes */
915 false /* checkMtime */))
cde41ae8 916 {
b2e465d6 917 return false;
cde41ae8 918 }
b2e465d6
AL
919
920 // Parse the package name
921 if (Package.empty() == true)
922 {
b2e465d6
AL
923 Package = Db.Control.Section.FindS("Package");
924 }
925
926 Db.Contents.Add(Gen,Package);
927
928 return Db.Finish();
929}
930 /*}}}*/
931// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
932// ---------------------------------------------------------------------
933/* */
9209ec47 934bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
b2e465d6
AL
935{
936 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
937 if (_error->PendingError() == true)
938 return false;
12d1f5b3 939
b2e465d6 940 // Open the package file
12d1f5b3
DK
941 FileFd Fd;
942 if (Pkgs.OpenOld(Fd) == false)
b2e465d6 943 return false;
12d1f5b3 944
b2e465d6
AL
945 pkgTagFile Tags(&Fd);
946 if (_error->PendingError() == true)
b2e465d6 947 return false;
12d1f5b3 948
b2e465d6
AL
949 // Parse.
950 pkgTagSection Section;
951 while (Tags.Step(Section) == true)
952 {
953 string File = flCombine(Prefix,Section.FindS("FileName"));
954 string Package = Section.FindS("Section");
955 if (Package.empty() == false && Package.end()[-1] != '/')
956 {
957 Package += '/';
958 Package += Section.FindS("Package");
959 }
960 else
961 Package += Section.FindS("Package");
962
963 DoPackage(File,Package);
964 if (_error->empty() == false)
965 {
966 _error->Error("Errors apply to file '%s'",File.c_str());
967 _error->DumpErrors();
968 }
969 }
12d1f5b3 970
b2e465d6 971 // Tidy the compressor
12d1f5b3
DK
972 Fd.Close();
973
b2e465d6
AL
974 return true;
975}
98953965 976
b2e465d6 977 /*}}}*/
98953965
AL
978
979// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
980// ---------------------------------------------------------------------
981/* */
65512241 982ReleaseWriter::ReleaseWriter(string const &/*DB*/)
98953965 983{
3cb3fe76
DK
984 if (_config->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
985 {
986 AddPattern("Packages");
987 AddPattern("Packages.gz");
988 AddPattern("Packages.bz2");
989 AddPattern("Packages.lzma");
b7080ced 990 AddPattern("Packages.xz");
8e3900d0 991 AddPattern("Translation-*");
3cb3fe76
DK
992 AddPattern("Sources");
993 AddPattern("Sources.gz");
994 AddPattern("Sources.bz2");
995 AddPattern("Sources.lzma");
b7080ced 996 AddPattern("Sources.xz");
3cb3fe76 997 AddPattern("Release");
0baf849d 998 AddPattern("Contents-*");
8d16c617 999 AddPattern("Index");
3cb3fe76
DK
1000 AddPattern("md5sum.txt");
1001 }
1002 AddPatterns(_config->FindVector("APT::FTPArchive::Release::Patterns"));
187492a6 1003
98953965 1004 Output = stdout;
9209ec47 1005 time_t const now = time(NULL);
cb12d0a6
DK
1006
1007 setlocale(LC_TIME, "C");
1008
98953965
AL
1009 char datestr[128];
1010 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
1011 gmtime(&now)) == 0)
1012 {
1013 datestr[0] = '\0';
1014 }
1015
c99e48ec 1016 time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
cdb623ed 1017 char validstr[128];
c99e48ec
DK
1018 if (now == validuntil ||
1019 strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
1020 gmtime(&validuntil)) == 0)
1021 {
cdb623ed 1022 validstr[0] = '\0';
c99e48ec
DK
1023 }
1024
cb12d0a6
DK
1025 setlocale(LC_TIME, "");
1026
98953965
AL
1027 map<string,string> Fields;
1028 Fields["Origin"] = "";
1029 Fields["Label"] = "";
1030 Fields["Suite"] = "";
1031 Fields["Version"] = "";
1032 Fields["Codename"] = "";
1033 Fields["Date"] = datestr;
c99e48ec 1034 Fields["Valid-Until"] = validstr;
98953965
AL
1035 Fields["Architectures"] = "";
1036 Fields["Components"] = "";
1037 Fields["Description"] = "";
1038
1039 for(map<string,string>::const_iterator I = Fields.begin();
1040 I != Fields.end();
1041 ++I)
1042 {
1043 string Config = string("APT::FTPArchive::Release::") + (*I).first;
1044 string Value = _config->Find(Config, (*I).second.c_str());
1045 if (Value == "")
1046 continue;
1047
1048 fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
1049 }
3c54407f 1050
a311fb96 1051 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Release");
98953965
AL
1052}
1053 /*}}}*/
1054// ReleaseWriter::DoPackage - Process a single package /*{{{*/
1055// ---------------------------------------------------------------------
1056bool ReleaseWriter::DoPackage(string FileName)
1057{
1058 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1059 string NewFileName;
1060 if (DirStrip.empty() == false &&
1061 FileName.length() > DirStrip.length() &&
1062 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
1063 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 1064 {
98953965 1065 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 1066 while (NewFileName[0] == '/')
9202409d 1067 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 1068 }
98953965
AL
1069 else
1070 NewFileName = FileName;
c0eb6bc6 1071
98953965
AL
1072 if (PathPrefix.empty() == false)
1073 NewFileName = flCombine(PathPrefix,NewFileName);
1074
1075 FileFd fd(FileName, FileFd::ReadOnly);
1076
1077 if (!fd.IsOpen())
1078 {
1079 return false;
1080 }
1081
c0eb6bc6 1082 CheckSums[NewFileName].size = fd.Size();
f7291f62 1083
8c4e1f97 1084 Hashes hs;
a311fb96
DK
1085 hs.AddFD(fd, 0, DoHashes);
1086 CheckSums[NewFileName].Hashes = hs.GetHashStringList();
98953965 1087 fd.Close();
8c4e1f97 1088
98953965
AL
1089 return true;
1090}
f7291f62
AL
1091
1092 /*}}}*/
1093// ReleaseWriter::Finish - Output the checksums /*{{{*/
1094// ---------------------------------------------------------------------
a311fb96 1095static void printChecksumTypeRecord(FILE * const Output, char const * const Type, map<string, ReleaseWriter::CheckSum> const &CheckSums)
f7291f62 1096{
a311fb96
DK
1097 fprintf(Output, "%s:\n", Type);
1098 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
3c54407f
DK
1099 I != CheckSums.end(); ++I)
1100 {
a311fb96
DK
1101 HashString const * const hs = I->second.Hashes.find(Type);
1102 if (hs == NULL)
1103 continue;
650faab0 1104 fprintf(Output, " %s %16llu %s\n",
a311fb96 1105 hs->HashValue().c_str(),
3c54407f
DK
1106 (*I).second.size,
1107 (*I).first.c_str());
1108 }
a311fb96
DK
1109}
1110void ReleaseWriter::Finish()
1111{
1112 if ((DoHashes & Hashes::MD5SUM) == Hashes::MD5SUM)
1113 printChecksumTypeRecord(Output, "MD5Sum", CheckSums);
1114 if ((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM)
1115 printChecksumTypeRecord(Output, "SHA1", CheckSums);
1116 if ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM)
1117 printChecksumTypeRecord(Output, "SHA256", CheckSums);
1118 if ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM)
1119 printChecksumTypeRecord(Output, "SHA512", CheckSums);
f7291f62 1120}