]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
fix 'Source' to 'Package' rename in apt-ftparchive
[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
b2e465d6
AL
443 // Strip the DirStrip prefix from the FileName and add the PathPrefix
444 string NewFileName;
445 if (DirStrip.empty() == false &&
446 FileName.length() > DirStrip.length() &&
447 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
448 DirStrip.begin(),DirStrip.end()) == 0)
449 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
450 else
451 NewFileName = FileName;
452 if (PathPrefix.empty() == false)
453 NewFileName = flCombine(PathPrefix,NewFileName);
9c24493f
DK
454
455 /* Configuration says we don't want to include the long Description
456 in the package file - instead we want to ship a separated file */
457 string desc;
458 if (LongDescription == false) {
459 desc = Tags.FindS("Description").append("\n");
460 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
461 }
462
b2e465d6 463 // This lists all the changes to the fields we are going to make.
eb0d90f0 464 std::vector<TFRewriteData> Changes;
64177f17 465
b8eba208
DK
466 std::string Size;
467 strprintf(Size, "%llu", (unsigned long long) FileSize);
468 Changes.push_back(SetTFRewriteData("Size", Size.c_str()));
469
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()));
23397c9d
DK
474 else if (hs->HashType() == "Checksum-FileSize")
475 continue;
a311fb96
DK
476 else
477 Changes.push_back(SetTFRewriteData(hs->HashType().c_str(), hs->HashValue().c_str()));
478 }
eb0d90f0
DK
479 Changes.push_back(SetTFRewriteData("Filename", NewFileName.c_str()));
480 Changes.push_back(SetTFRewriteData("Priority", OverItem->Priority.c_str()));
481 Changes.push_back(SetTFRewriteData("Status", 0));
482 Changes.push_back(SetTFRewriteData("Optional", 0));
64177f17 483
9c24493f
DK
484 string DescriptionMd5;
485 if (LongDescription == false) {
486 MD5Summation descmd5;
487 descmd5.Add(desc.c_str());
488 DescriptionMd5 = descmd5.Result().Value();
eb0d90f0 489 Changes.push_back(SetTFRewriteData("Description-md5", DescriptionMd5.c_str()));
66905344
DK
490 if (TransWriter != NULL)
491 TransWriter->DoPackage(Package, desc, DescriptionMd5);
9c24493f
DK
492 }
493
b2e465d6
AL
494 // Rewrite the maintainer field if necessary
495 bool MaintFailed;
496 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
497 if (MaintFailed == true)
498 {
499 if (NoOverride == false)
500 {
501 NewLine(1);
dc738e7a
AL
502 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
503 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
eb0d90f0 504 }
b2e465d6 505 }
eb0d90f0 506
b2e465d6 507 if (NewMaint.empty() == false)
eb0d90f0
DK
508 Changes.push_back(SetTFRewriteData("Maintainer", NewMaint.c_str()));
509
b2e465d6 510 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
c6474fb6 511 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
b2e465d6
AL
512 but dpkg does this append bit. So we do the append bit, at least that way the
513 status file and package file will remain similar. There are other transforms
514 but optional is the only legacy one still in use for some lazy reason. */
515 string OptionalStr = Tags.FindS("Optional");
516 if (OptionalStr.empty() == false)
517 {
518 if (Tags.FindS("Suggests").empty() == false)
519 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
eb0d90f0 520 Changes.push_back(SetTFRewriteData("Suggests", OptionalStr.c_str()));
b2e465d6 521 }
64177f17 522
eb0d90f0 523 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
f7f0d6c7 524 I != OverItem->FieldOverride.end(); ++I)
eb0d90f0 525 Changes.push_back(SetTFRewriteData(I->first.c_str(),I->second.c_str()));
64177f17 526
eb0d90f0 527 Changes.push_back(SetTFRewriteData( 0, 0));
64177f17 528
b2e465d6 529 // Rewrite and store the fields.
eb0d90f0 530 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes.data()) == false)
b2e465d6
AL
531 return false;
532 fprintf(Output,"\n");
533
534 return Db.Finish();
535}
536 /*}}}*/
537
66905344
DK
538// TranslationWriter::TranslationWriter - Constructor /*{{{*/
539// ---------------------------------------------------------------------
540/* Create a Translation-Master file for this Packages file */
34f1d96c
DK
541TranslationWriter::TranslationWriter(string const &File, string const &TransCompress,
542 mode_t const &Permissions) : Output(NULL),
66905344
DK
543 RefCounter(0)
544{
545 if (File.empty() == true)
546 return;
547
34f1d96c
DK
548 Comp = new MultiCompress(File, TransCompress, Permissions);
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
568 fprintf(Output, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
569 Pkg.c_str(), MD5.c_str(), Desc.c_str());
570
571 Included.insert(Record);
572 return true;
573}
574 /*}}}*/
575// TranslationWriter::~TranslationWriter - Destructor /*{{{*/
576// ---------------------------------------------------------------------
577/* */
578TranslationWriter::~TranslationWriter()
579{
34f1d96c
DK
580 if (Comp == NULL)
581 return;
582
583 delete Comp;
66905344
DK
584}
585 /*}}}*/
586
b2e465d6
AL
587// SourcesWriter::SourcesWriter - Constructor /*{{{*/
588// ---------------------------------------------------------------------
589/* */
f6f06a8f
MV
590SourcesWriter::SourcesWriter(string const &DB, string const &BOverrides,string const &SOverrides,
591 string const &ExtOverrides) :
592 Db(DB), Stats(Db.Stats)
b2e465d6
AL
593{
594 Output = stdout;
98953965 595 AddPattern("*.dsc");
b2e465d6
AL
596 DeLinkLimit = 0;
597 Buffer = 0;
598 BufSize = 0;
599
600 // Process the command line options
a311fb96 601 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Sources");
b2e465d6 602 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
f6f06a8f 603 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
604
605 // Read the override file
606 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
607 return;
608 else
609 NoOverride = true;
64177f17 610
cde41ae8
MV
611 // WTF?? The logic above: if we can't read binary overrides, don't even try
612 // reading source overrides. if we can read binary overrides, then say there
613 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
614
64177f17
AL
615 if (ExtOverrides.empty() == false)
616 SOver.ReadExtraOverride(ExtOverrides);
b2e465d6 617
64177f17
AL
618 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
619 SOver.ReadOverride(SOverrides,true);
b2e465d6
AL
620}
621 /*}}}*/
622// SourcesWriter::DoPackage - Process a single package /*{{{*/
a311fb96
DK
623static std::ostream& addDscHash(std::ostream &out, unsigned int const DoHashes,
624 Hashes::SupportedHashes const DoIt, pkgTagSection &Tags, char const * const FieldName,
625 HashString const * const Hash, unsigned long long Size, std::string FileName)
626{
627 if ((DoHashes & DoIt) != DoIt || Tags.Exists(FieldName) == false || Hash == NULL)
628 return out;
629 out << "\n " << Hash->HashValue() << " " << Size << " " << FileName
630 << "\n " << Tags.FindS(FieldName);
631 return out;
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];
0b41e0e7 663 auto_ptr<Override::Item> OverItem(0);
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 {
0b41e0e7
MV
676 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
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)
688 OverItem = 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
0b41e0e7 701 OverItem = auto_ptr<Override::Item>(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
0b41e0e7 708 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
cde41ae8 709 // const auto_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());
0b41e0e7
MV
713 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
714 if (SOverItem.get() == 0)
715 {
cde41ae8 716 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
717 SOverItem = auto_ptr<Override::Item>(new Override::Item);
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);
bf99a6d3 724 std::ostringstream ostreamFiles;
a311fb96 725 addDscHash(ostreamFiles, DoHashes, Hashes::MD5SUM, Tags, "Files", Db.HashesList.find("MD5Sum"), St.st_size, strippedName);
bf99a6d3
DK
726 string const Files = ostreamFiles.str();
727
728 std::ostringstream ostreamSha1;
a311fb96 729 addDscHash(ostreamSha1, DoHashes, Hashes::SHA1SUM, Tags, "Checksums-Sha1", Db.HashesList.find("SHA1"), St.st_size, strippedName);
bf99a6d3 730 std::ostringstream ostreamSha256;
a311fb96 731 addDscHash(ostreamSha256, DoHashes, Hashes::SHA256SUM, Tags, "Checksums-Sha256", Db.HashesList.find("SHA256"), St.st_size, strippedName);
9a961efc 732 std::ostringstream ostreamSha512;
a311fb96 733 addDscHash(ostreamSha512, DoHashes, Hashes::SHA512SUM, Tags, "Checksums-Sha512", Db.HashesList.find("SHA512"), St.st_size, strippedName);
9a961efc 734
b2e465d6
AL
735 // Strip the DirStrip prefix from the FileName and add the PathPrefix
736 string NewFileName;
737 if (DirStrip.empty() == false &&
738 FileName.length() > DirStrip.length() &&
8c58f506 739 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
b2e465d6
AL
740 NewFileName = string(OriginalPath + DirStrip.length());
741 else
742 NewFileName = OriginalPath;
743 if (PathPrefix.empty() == false)
744 NewFileName = flCombine(PathPrefix,NewFileName);
171c45bc 745
b2e465d6
AL
746 string Directory = flNotFile(OriginalPath);
747 string Package = Tags.FindS("Source");
171c45bc 748
f6f06a8f 749 // Perform operation over all of the files
b2e465d6 750 string ParseJnk;
bf99a6d3 751 const char *C = Files.c_str();
ab3846c0 752 char *RealPath = NULL;
b2e465d6
AL
753 for (;isspace(*C); C++);
754 while (*C != 0)
755 {
756 // Parse each of the elements
757 if (ParseQuoteWord(C,ParseJnk) == false ||
758 ParseQuoteWord(C,ParseJnk) == false ||
759 ParseQuoteWord(C,ParseJnk) == false)
760 return _error->Error("Error parsing file record");
f6f06a8f 761
b2e465d6 762 string OriginalPath = Directory + ParseJnk;
f6f06a8f
MV
763
764 // Add missing hashes to source files
a311fb96
DK
765 if (((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM && !Tags.Exists("Checksums-Sha1")) ||
766 ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM && !Tags.Exists("Checksums-Sha256")) ||
767 ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM && !Tags.Exists("Checksums-Sha512")))
f6f06a8f 768 {
a311fb96 769 if (Db.GetFileInfo(OriginalPath,
ce928105
MV
770 false, /* DoControl */
771 false, /* DoContents */
772 false, /* GenContentsOnly */
773 false, /* DoSource */
a311fb96 774 DoHashes,
ce928105 775 DoAlwaysStat) == false)
f6f06a8f
MV
776 {
777 return _error->Error("Error getting file info");
778 }
779
a311fb96
DK
780 for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
781 {
23397c9d 782 if (hs->HashType() == "MD5Sum" || hs->HashType() == "Checksum-FileSize")
a311fb96
DK
783 continue;
784 char const * fieldname;
785 std::ostream * out;
786 if (hs->HashType() == "SHA1")
787 {
788 fieldname = "Checksums-Sha1";
789 out = &ostreamSha1;
790 }
791 else if (hs->HashType() == "SHA256")
792 {
793 fieldname = "Checksums-Sha256";
794 out = &ostreamSha256;
795 }
796 else if (hs->HashType() == "SHA512")
797 {
798 fieldname = "Checksums-Sha512";
799 out = &ostreamSha512;
800 }
801 else
802 {
803 _error->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs->HashType().c_str());
804 continue;
805 }
806 if (Tags.Exists(fieldname) == true)
807 continue;
808 (*out) << "\n " << hs->HashValue() << " " << Db.GetFileSize() << " " << ParseJnk;
809 }
ce928105 810
a311fb96
DK
811 // write back the GetFileInfo() stats data
812 Db.Finish();
f6f06a8f
MV
813 }
814
815 // Perform the delinking operation
816 char Jnk[2];
817
ab3846c0
MV
818 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
819 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
b2e465d6
AL
820 {
821 string RP = RealPath;
ab3846c0 822 free(RealPath);
cde41ae8 823 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
b2e465d6
AL
824 return false;
825 }
826 }
827
828 Directory = flNotFile(NewFileName);
829 if (Directory.length() > 2)
830 Directory.erase(Directory.end()-1);
171c45bc 831
f6f06a8f
MV
832 string const ChecksumsSha1 = ostreamSha1.str();
833 string const ChecksumsSha256 = ostreamSha256.str();
834 string const ChecksumsSha512 = ostreamSha512.str();
835
b2e465d6 836 // This lists all the changes to the fields we are going to make.
f99da908 837 // (5 hardcoded + checksums + maintainer + end marker)
eb0d90f0 838 std::vector<TFRewriteData> Changes;
64177f17 839
5f4fcd88
DK
840 Changes.push_back(SetTFRewriteData("Source", 0));
841 Changes.push_back(SetTFRewriteData("Package",Package.c_str()));
3c54407f 842 if (Files.empty() == false)
eb0d90f0 843 Changes.push_back(SetTFRewriteData("Files",Files.c_str()));
3c54407f 844 if (ChecksumsSha1.empty() == false)
eb0d90f0 845 Changes.push_back(SetTFRewriteData("Checksums-Sha1",ChecksumsSha1.c_str()));
3c54407f 846 if (ChecksumsSha256.empty() == false)
eb0d90f0 847 Changes.push_back(SetTFRewriteData("Checksums-Sha256",ChecksumsSha256.c_str()));
12cd178d 848 if (ChecksumsSha512.empty() == false)
eb0d90f0 849 Changes.push_back(SetTFRewriteData("Checksums-Sha512",ChecksumsSha512.c_str()));
171c45bc 850 if (Directory != "./")
eb0d90f0
DK
851 Changes.push_back(SetTFRewriteData("Directory",Directory.c_str()));
852 Changes.push_back(SetTFRewriteData("Priority",BestPrio.c_str()));
853 Changes.push_back(SetTFRewriteData("Status",0));
b2e465d6
AL
854
855 // Rewrite the maintainer field if necessary
856 bool MaintFailed;
857 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
858 if (MaintFailed == true)
859 {
860 if (NoOverride == false)
861 {
862 NewLine(1);
dc738e7a
AL
863 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
864 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
b2e465d6
AL
865 }
866 }
867 if (NewMaint.empty() == false)
eb0d90f0 868 Changes.push_back(SetTFRewriteData("Maintainer", NewMaint.c_str()));
64177f17 869
9209ec47 870 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
f7f0d6c7 871 I != SOverItem->FieldOverride.end(); ++I)
eb0d90f0 872 Changes.push_back(SetTFRewriteData(I->first.c_str(),I->second.c_str()));
64177f17 873
eb0d90f0 874 Changes.push_back(SetTFRewriteData(0, 0));
b2e465d6
AL
875
876 // Rewrite and store the fields.
eb0d90f0 877 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes.data()) == false)
b2e465d6
AL
878 return false;
879 fprintf(Output,"\n");
880
881 Stats.Packages++;
882
ce928105 883 return true;
b2e465d6
AL
884}
885 /*}}}*/
886
887// ContentsWriter::ContentsWriter - Constructor /*{{{*/
888// ---------------------------------------------------------------------
889/* */
31981076
DK
890ContentsWriter::ContentsWriter(string const &DB, string const &Arch) :
891 FTWScanner(Arch), Db(DB), Stats(Db.Stats)
b2e465d6
AL
892
893{
31981076 894 SetExts(".deb");
b2e465d6
AL
895 Output = stdout;
896}
897 /*}}}*/
898// ContentsWriter::DoPackage - Process a single package /*{{{*/
899// ---------------------------------------------------------------------
900/* If Package is the empty string the control record will be parsed to
901 determine what the package name is. */
9209ec47 902bool ContentsWriter::DoPackage(string FileName, string Package)
b2e465d6 903{
a311fb96
DK
904 if (!Db.GetFileInfo(FileName,
905 Package.empty(), /* DoControl */
906 true, /* DoContents */
907 false, /* GenContentsOnly */
908 false, /* DoSource */
909 0, /* DoHashes */
910 false /* checkMtime */))
cde41ae8 911 {
b2e465d6 912 return false;
cde41ae8 913 }
b2e465d6
AL
914
915 // Parse the package name
916 if (Package.empty() == true)
917 {
b2e465d6
AL
918 Package = Db.Control.Section.FindS("Package");
919 }
920
921 Db.Contents.Add(Gen,Package);
922
923 return Db.Finish();
924}
925 /*}}}*/
926// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
927// ---------------------------------------------------------------------
928/* */
9209ec47 929bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
b2e465d6
AL
930{
931 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
932 if (_error->PendingError() == true)
933 return false;
12d1f5b3 934
b2e465d6 935 // Open the package file
12d1f5b3
DK
936 FileFd Fd;
937 if (Pkgs.OpenOld(Fd) == false)
b2e465d6 938 return false;
12d1f5b3 939
b2e465d6
AL
940 pkgTagFile Tags(&Fd);
941 if (_error->PendingError() == true)
b2e465d6 942 return false;
12d1f5b3 943
b2e465d6
AL
944 // Parse.
945 pkgTagSection Section;
946 while (Tags.Step(Section) == true)
947 {
948 string File = flCombine(Prefix,Section.FindS("FileName"));
949 string Package = Section.FindS("Section");
950 if (Package.empty() == false && Package.end()[-1] != '/')
951 {
952 Package += '/';
953 Package += Section.FindS("Package");
954 }
955 else
956 Package += Section.FindS("Package");
957
958 DoPackage(File,Package);
959 if (_error->empty() == false)
960 {
961 _error->Error("Errors apply to file '%s'",File.c_str());
962 _error->DumpErrors();
963 }
964 }
12d1f5b3 965
b2e465d6 966 // Tidy the compressor
12d1f5b3
DK
967 Fd.Close();
968
b2e465d6
AL
969 return true;
970}
98953965 971
b2e465d6 972 /*}}}*/
98953965
AL
973
974// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
975// ---------------------------------------------------------------------
976/* */
65512241 977ReleaseWriter::ReleaseWriter(string const &/*DB*/)
98953965 978{
3cb3fe76
DK
979 if (_config->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
980 {
981 AddPattern("Packages");
982 AddPattern("Packages.gz");
983 AddPattern("Packages.bz2");
984 AddPattern("Packages.lzma");
b7080ced 985 AddPattern("Packages.xz");
8e3900d0 986 AddPattern("Translation-*");
3cb3fe76
DK
987 AddPattern("Sources");
988 AddPattern("Sources.gz");
989 AddPattern("Sources.bz2");
990 AddPattern("Sources.lzma");
b7080ced 991 AddPattern("Sources.xz");
3cb3fe76 992 AddPattern("Release");
0baf849d 993 AddPattern("Contents-*");
8d16c617 994 AddPattern("Index");
3cb3fe76
DK
995 AddPattern("md5sum.txt");
996 }
997 AddPatterns(_config->FindVector("APT::FTPArchive::Release::Patterns"));
187492a6 998
98953965 999 Output = stdout;
9209ec47 1000 time_t const now = time(NULL);
cb12d0a6
DK
1001
1002 setlocale(LC_TIME, "C");
1003
98953965
AL
1004 char datestr[128];
1005 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
1006 gmtime(&now)) == 0)
1007 {
1008 datestr[0] = '\0';
1009 }
1010
c99e48ec 1011 time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
cdb623ed 1012 char validstr[128];
c99e48ec
DK
1013 if (now == validuntil ||
1014 strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
1015 gmtime(&validuntil)) == 0)
1016 {
cdb623ed 1017 validstr[0] = '\0';
c99e48ec
DK
1018 }
1019
cb12d0a6
DK
1020 setlocale(LC_TIME, "");
1021
98953965
AL
1022 map<string,string> Fields;
1023 Fields["Origin"] = "";
1024 Fields["Label"] = "";
1025 Fields["Suite"] = "";
1026 Fields["Version"] = "";
1027 Fields["Codename"] = "";
1028 Fields["Date"] = datestr;
c99e48ec 1029 Fields["Valid-Until"] = validstr;
98953965
AL
1030 Fields["Architectures"] = "";
1031 Fields["Components"] = "";
1032 Fields["Description"] = "";
1033
1034 for(map<string,string>::const_iterator I = Fields.begin();
1035 I != Fields.end();
1036 ++I)
1037 {
1038 string Config = string("APT::FTPArchive::Release::") + (*I).first;
1039 string Value = _config->Find(Config, (*I).second.c_str());
1040 if (Value == "")
1041 continue;
1042
1043 fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
1044 }
3c54407f 1045
a311fb96 1046 ConfigToDoHashes(DoHashes, "APT::FTPArchive::Release");
98953965
AL
1047}
1048 /*}}}*/
1049// ReleaseWriter::DoPackage - Process a single package /*{{{*/
1050// ---------------------------------------------------------------------
1051bool ReleaseWriter::DoPackage(string FileName)
1052{
1053 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1054 string NewFileName;
1055 if (DirStrip.empty() == false &&
1056 FileName.length() > DirStrip.length() &&
1057 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
1058 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 1059 {
98953965 1060 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 1061 while (NewFileName[0] == '/')
9202409d 1062 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 1063 }
98953965
AL
1064 else
1065 NewFileName = FileName;
c0eb6bc6 1066
98953965
AL
1067 if (PathPrefix.empty() == false)
1068 NewFileName = flCombine(PathPrefix,NewFileName);
1069
1070 FileFd fd(FileName, FileFd::ReadOnly);
1071
1072 if (!fd.IsOpen())
1073 {
1074 return false;
1075 }
1076
c0eb6bc6 1077 CheckSums[NewFileName].size = fd.Size();
f7291f62 1078
9224ce3d
DK
1079 Hashes hs(DoHashes);
1080 hs.AddFD(fd);
a311fb96 1081 CheckSums[NewFileName].Hashes = hs.GetHashStringList();
98953965 1082 fd.Close();
8c4e1f97 1083
98953965
AL
1084 return true;
1085}
f7291f62
AL
1086
1087 /*}}}*/
1088// ReleaseWriter::Finish - Output the checksums /*{{{*/
1089// ---------------------------------------------------------------------
a311fb96 1090static void printChecksumTypeRecord(FILE * const Output, char const * const Type, map<string, ReleaseWriter::CheckSum> const &CheckSums)
f7291f62 1091{
a311fb96
DK
1092 fprintf(Output, "%s:\n", Type);
1093 for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
3c54407f
DK
1094 I != CheckSums.end(); ++I)
1095 {
a311fb96
DK
1096 HashString const * const hs = I->second.Hashes.find(Type);
1097 if (hs == NULL)
1098 continue;
650faab0 1099 fprintf(Output, " %s %16llu %s\n",
a311fb96 1100 hs->HashValue().c_str(),
3c54407f
DK
1101 (*I).second.size,
1102 (*I).first.c_str());
1103 }
a311fb96
DK
1104}
1105void ReleaseWriter::Finish()
1106{
1107 if ((DoHashes & Hashes::MD5SUM) == Hashes::MD5SUM)
1108 printChecksumTypeRecord(Output, "MD5Sum", CheckSums);
1109 if ((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM)
1110 printChecksumTypeRecord(Output, "SHA1", CheckSums);
1111 if ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM)
1112 printChecksumTypeRecord(Output, "SHA256", CheckSums);
1113 if ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM)
1114 printChecksumTypeRecord(Output, "SHA512", CheckSums);
f7291f62 1115}