// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: writer.cc,v 1.4 2001/06/26 02:50:27 jgg Exp $
+// $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
/* ######################################################################
Writer
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
-#ifdef __GNUG__
-#pragma implementation "writer.h"
-#endif
-
#include "writer.h"
+#include <apti18n.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/error.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/sha256.h>
#include <apt-pkg/deblistparser.h>
#include <sys/types.h>
#include <unistd.h>
+#include <ctime>
#include <ftw.h>
+#include <fnmatch.h>
#include <iostream>
+#include <memory>
#include "cachedb.h"
#include "apt-ftparchive.h"
#include "multicompress.h"
/*}}}*/
-
using namespace std;
FTWScanner *FTWScanner::Owner;
{
ErrorPrinted = false;
NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
- TmpExt = 0;
- Ext[0] = 0;
RealPath = 0;
long PMax = pathconf(".",_PC_PATH_MAX);
if (PMax > 0)
// ---------------------------------------------------------------------
/* This is the FTW scanner, it processes each directory element in the
directory tree. */
-int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
+int FTWScanner::ScannerFTW(const char *File,const struct stat *sb,int Flag)
{
if (Flag == FTW_DNR)
{
Owner->NewLine(1);
- c1out << "W: Unable to read directory " << File << endl;
+ ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
}
if (Flag == FTW_NS)
{
Owner->NewLine(1);
- c1out << "W: Unable to stat " << File << endl;
+ ioprintf(c1out, _("W: Unable to stat %s\n"), File);
}
if (Flag != FTW_F)
return 0;
- // See if it is a .deb
- if (strlen(File) < 4)
- return 0;
-
- unsigned CurExt = 0;
- for (; Owner->Ext[CurExt] != 0; CurExt++)
- if (strcmp(File+strlen(File)-strlen(Owner->Ext[CurExt]),
- Owner->Ext[CurExt]) == 0)
- break;
- if (Owner->Ext[CurExt] == 0)
+ return ScannerFile(File, true);
+}
+ /*}}}*/
+// FTWScanner::ScannerFile - File Scanner /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+int FTWScanner::ScannerFile(const char *File, bool ReadLink)
+{
+ const char *LastComponent = strrchr(File, '/');
+ if (LastComponent == NULL)
+ LastComponent = File;
+ else
+ LastComponent++;
+
+ vector<string>::iterator I;
+ for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
+ {
+ if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
+ break;
+ }
+ if (I == Owner->Patterns.end())
return 0;
/* Process it. If the file is a link then resolve it into an absolute
given are not links themselves. */
char Jnk[2];
Owner->OriginalPath = File;
- if (Owner->RealPath != 0 && readlink(File,Jnk,sizeof(Jnk)) != -1 &&
+ if (ReadLink && Owner->RealPath != 0 &&
+ readlink(File,Jnk,sizeof(Jnk)) != -1 &&
realpath(File,Owner->RealPath) != 0)
Owner->DoPackage(Owner->RealPath);
else
bool Type = _error->PopMessage(Err);
if (Type == true)
- c1out << "E: " << Err << endl;
+ cerr << _("E: ") << Err << endl;
else
- c1out << "W: " << Err << endl;
+ cerr << _("W: ") << Err << endl;
if (Err.find(File) != string::npos)
SeenPath = true;
}
if (SeenPath == false)
- cerr << "E: Errors apply to file '" << File << "'" << endl;
+ cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
return 0;
}
if (InternalPrefix.empty() == true)
{
if (realpath(Dir.c_str(),RealPath) == 0)
- return _error->Errno("realpath","Failed to resolve %s",Dir.c_str());
+ return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
InternalPrefix = RealPath;
}
// Do recursive directory searching
Owner = this;
- int Res = ftw(Dir.c_str(),Scanner,30);
+ int Res = ftw(Dir.c_str(),ScannerFTW,30);
// Error treewalking?
if (Res != 0)
{
if (_error->PendingError() == false)
- _error->Errno("ftw","Tree walking failed");
+ _error->Errno("ftw",_("Tree walking failed"));
return false;
}
if (InternalPrefix.empty() == true)
{
if (realpath(Dir.c_str(),RealPath) == 0)
- return _error->Errno("realpath","Failed to resolve %s",Dir.c_str());
+ return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
InternalPrefix = RealPath;
}
Owner = this;
FILE *List = fopen(File.c_str(),"r");
if (List == 0)
- return _error->Errno("fopen","Failed to open %s",File.c_str());
+ return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
/* We are a tad tricky here.. We prefix the buffer with the directory
name, that way if we need a full path with just use line.. Sneaky and
FileName = Line;
}
+#if 0
struct stat St;
int Flag = FTW_F;
if (stat(FileName,&St) != 0)
Flag = FTW_NS;
+#endif
- if (Scanner(FileName,&St,Flag) != 0)
+ if (ScannerFile(FileName, false) != 0)
break;
}
/* */
bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
unsigned long &DeLinkBytes,
- struct stat &St)
+ off_t FileSize)
{
// See if this isn't an internaly prefix'd file name.
if (InternalPrefix.empty() == false &&
cout << endl;
NewLine(1);
- c1out << " DeLink " << (OriginalPath + InternalPrefix.length())
- << " [" << SizeToStr(St.st_size) << "B]" << endl << flush;
+ ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
+ SizeToStr(FileSize).c_str());
+ c1out << flush;
if (NoLinkAct == false)
{
char OldLink[400];
if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
- _error->Errno("readlink","Failed to readlink %s",OriginalPath);
+ _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
else
{
if (unlink(OriginalPath) != 0)
- _error->Errno("unlink","Failed to unlink %s",OriginalPath);
+ _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
else
{
if (link(FileName.c_str(),OriginalPath) != 0)
{
// Panic! Restore the symlink
symlink(OldLink,OriginalPath);
- return _error->Errno("link","*** Failed to link %s to %s",
+ return _error->Errno("link",_("*** Failed to link %s to %s"),
FileName.c_str(),
OriginalPath);
}
}
}
- DeLinkBytes += St.st_size;
+ DeLinkBytes += FileSize;
if (DeLinkBytes/1024 >= DeLinkLimit)
- c1out << " DeLink limit of " << SizeToStr(DeLinkBytes) << "B hit." << endl;
+ ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
}
FileName = OriginalPath;
return true;
}
/*}}}*/
-// FTWScanner::SetExts - Set extensions to support /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool FTWScanner::SetExts(string Vals)
-{
- delete [] TmpExt;
- TmpExt = new char[Vals.length()+1];
- strcpy(TmpExt,Vals.c_str());
- return TokSplitString(' ',TmpExt,(char **)Ext,sizeof(Ext)/sizeof(Ext[0]));
-}
- /*}}}*/
// PackagesWriter::PackagesWriter - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
-PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides) :
- Db(DB),Stats(Db.Stats)
+PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides,
+ string aArch) :
+ Db(DB),Stats(Db.Stats), Arch(aArch)
{
Output = stdout;
- Ext[0] = ".deb";
- Ext[1] = 0;
+ SetExts(".deb .udeb .foo .bar .baz");
+ AddPattern("*.deb");
DeLinkLimit = 0;
// Process the command line options
DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
+ DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
+ DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
DoContents = _config->FindB("APT::FTPArchive::Contents",true);
NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
_error->DumpErrors();
}
+ /*}}}*/
+// FTWScanner::SetExts - Set extensions to support /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FTWScanner::SetExts(string Vals)
+{
+ ClearPatterns();
+ string::size_type Start = 0;
+ while (Start <= Vals.length()-1)
+ {
+ string::size_type Space = Vals.find(' ',Start);
+ string::size_type Length;
+ if (Space == string::npos)
+ {
+ Length = Vals.length()-Start;
+ }
+ else
+ {
+ Length = Space-Start;
+ }
+ AddPattern(string("*") + Vals.substr(Start, Length));
+ Start += Length + 1;
+ }
+
+ return true;
+}
+
/*}}}*/
// PackagesWriter::DoPackage - Process a single package /*{{{*/
// ---------------------------------------------------------------------
/* This method takes a package and gets its control information and
- MD5 then writes out a control record with the proper fields rewritten
- and the path/size/hash appended. */
+ MD5, SHA1 and SHA256 then writes out a control record with the proper fields
+ rewritten and the path/size/hash appended. */
bool PackagesWriter::DoPackage(string FileName)
{
- // Open the archive
- FileFd F(FileName,FileFd::ReadOnly);
- if (_error->PendingError() == true)
- return false;
-
- // Stat the file for later
- struct stat St;
- if (fstat(F.Fd(),&St) != 0)
- return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
-
// Pull all the data we need form the DB
- string MD5Res;
- if (Db.SetFile(FileName,St,&F) == false ||
- Db.LoadControl() == false ||
- (DoContents == true && Db.LoadContents(true) == false) ||
- (DoMD5 == true && Db.GetMD5(MD5Res,false) == false))
+ if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256)
+ == false)
+ {
return false;
+ }
- if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,St) == false)
+ off_t FileSize = Db.GetFileSize();
+ if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
return false;
// Lookup the overide information
pkgTagSection &Tags = Db.Control.Section;
string Package = Tags.FindS("Package");
- Override::Item Tmp;
- Override::Item *OverItem = Over.GetItem(Package);
+ string Architecture;
+ // if we generate a Packages file for a given arch, we use it to
+ // look for overrides. if we run in "simple" mode without the
+ // "Architecures" variable in the config we use the architecure value
+ // from the deb file
+ if(Arch != "")
+ Architecture = Arch;
+ else
+ Architecture = Tags.FindS("Architecture");
+ auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
if (Package.empty() == true)
- return _error->Error("Archive had no package field");
-
+ return _error->Error(_("Archive had no package field"));
+
// If we need to do any rewriting of the header do it now..
- if (OverItem == 0)
+ if (OverItem.get() == 0)
{
if (NoOverride == false)
{
NewLine(1);
- c1out << " " << Package << " has no override entry" << endl;
+ ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
}
- OverItem = &Tmp;
- Tmp.FieldOverride["Section"] = Tags.FindS("Section");
- Tmp.Priority = Tags.FindS("Priority");
+ OverItem = auto_ptr<Override::Item>(new Override::Item);
+ OverItem->FieldOverride["Section"] = Tags.FindS("Section");
+ OverItem->Priority = Tags.FindS("Priority");
}
char Size[40];
- sprintf(Size,"%lu",St.st_size);
+ sprintf(Size,"%lu", (unsigned long) FileSize);
// Strip the DirStrip prefix from the FileName and add the PathPrefix
string NewFileName;
unsigned int End = 0;
SetTFRewriteData(Changes[End++], "Size", Size);
- SetTFRewriteData(Changes[End++], "MD5sum", MD5Res.c_str());
+ SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
+ SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
+ SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
SetTFRewriteData(Changes[End++], "Status", 0);
if (NoOverride == false)
{
NewLine(1);
- c1out << " " << Package << " maintainer is " <<
- Tags.FindS("Maintainer") << " not " <<
- OverItem->OldMaint << endl;
+ ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
+ Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
}
}
string ExtOverrides)
{
Output = stdout;
- Ext[0] = ".dsc";
- Ext[1] = 0;
+ AddPattern("*.dsc");
DeLinkLimit = 0;
Buffer = 0;
BufSize = 0;
else
NoOverride = true;
+ // WTF?? The logic above: if we can't read binary overrides, don't even try
+ // reading source overrides. if we can read binary overrides, then say there
+ // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
+
if (ExtOverrides.empty() == false)
SOver.ReadExtraOverride(ExtOverrides);
// Lookup the overide information, finding first the best priority.
string BestPrio;
- char Buffer[1000];
string Bins = Tags.FindS("Binary");
- Override::Item *OverItem = 0;
- if (Bins.empty() == false && Bins.length() < sizeof(Buffer))
+ char Buffer[Bins.length() + 1];
+ auto_ptr<Override::Item> OverItem(0);
+ if (Bins.empty() == false)
{
strcpy(Buffer,Bins.c_str());
unsigned char BestPrioV = pkgCache::State::Extra;
for (unsigned I = 0; BinList[I] != 0; I++)
{
- Override::Item *Itm = BOver.GetItem(BinList[I]);
- if (Itm == 0)
+ auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
+ if (Itm.get() == 0)
continue;
- if (OverItem == 0)
- OverItem = Itm;
unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
if (NewPrioV < BestPrioV || BestPrio.empty() == true)
BestPrioV = NewPrioV;
BestPrio = Itm->Priority;
}
+
+ if (OverItem.get() == 0)
+ OverItem = Itm;
}
}
// If we need to do any rewriting of the header do it now..
- Override::Item Tmp;
- if (OverItem == 0)
+ if (OverItem.get() == 0)
{
if (NoOverride == false)
{
NewLine(1);
- c1out << " " << Tags.FindS("Source") << " has no override entry" << endl;
+ ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
}
- OverItem = &Tmp;
+ OverItem = auto_ptr<Override::Item>(new Override::Item);
}
- Override::Item *SOverItem = SOver.GetItem(Tags.FindS("Source"));
- if (SOverItem == 0)
+ auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
+ // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
+ if (SOverItem.get() == 0)
{
- SOverItem = BOver.GetItem(Tags.FindS("Source"));
- if (SOverItem == 0)
- SOverItem = OverItem;
+ ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
+ SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
+ if (SOverItem.get() == 0)
+ {
+ ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
+ SOverItem = auto_ptr<Override::Item>(new Override::Item);
+ *SOverItem = *OverItem;
+ }
}
// Add the dsc to the files hash list
NewFileName = OriginalPath;
if (PathPrefix.empty() == false)
NewFileName = flCombine(PathPrefix,NewFileName);
-
+
string Directory = flNotFile(OriginalPath);
string Package = Tags.FindS("Source");
-
+
// Perform the delinking operation over all of the files
string ParseJnk;
const char *C = Files;
realpath(OriginalPath.c_str(),RealPath) != 0)
{
string RP = RealPath;
- if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St) == false)
+ if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
return false;
}
}
Directory = flNotFile(NewFileName);
if (Directory.length() > 2)
Directory.erase(Directory.end()-1);
-
+
// This lists all the changes to the fields we are going to make.
// (5 hardcoded + maintainer + end marker)
TFRewriteData Changes[5+1+SOverItem->FieldOverride.size()+1];
unsigned int End = 0;
SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
SetTFRewriteData(Changes[End++],"Files",Files);
- SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
+ if (Directory != "./")
+ SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
SetTFRewriteData(Changes[End++],"Status",0);
if (NoOverride == false)
{
NewLine(1);
- c1out << " " << Package << " maintainer is " <<
- Tags.FindS("Maintainer") << " not " <<
- OverItem->OldMaint << endl;
+ ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
+ Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
}
}
if (NewMaint.empty() == false)
Db(DB), Stats(Db.Stats)
{
- Ext[0] = ".deb";
- Ext[1] = 0;
+ AddPattern("*.deb");
Output = stdout;
}
/*}}}*/
determine what the package name is. */
bool ContentsWriter::DoPackage(string FileName,string Package)
{
- // Open the archive
- FileFd F(FileName,FileFd::ReadOnly);
- if (_error->PendingError() == true)
- return false;
-
- // Stat the file for later
- struct stat St;
- if (fstat(F.Fd(),&St) != 0)
- return _error->Errno("fstat","Failed too stat %s",FileName.c_str());
-
- // Ready the DB
- if (Db.SetFile(FileName,St,&F) == false ||
- Db.LoadContents(false) == false)
+ if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false))
+ {
return false;
+ }
// Parse the package name
if (Package.empty() == true)
{
- if (Db.LoadControl() == false)
- return false;
Package = Db.Control.Section.FindS("Package");
}
// Open the package file
int CompFd = -1;
- int Proc = -1;
+ pid_t Proc = -1;
if (Pkgs.OpenOld(CompFd,Proc) == false)
return false;
return true;
}
+
+ /*}}}*/
+
+// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ReleaseWriter::ReleaseWriter(string DB)
+{
+ AddPattern("Packages");
+ AddPattern("Packages.gz");
+ AddPattern("Packages.bz2");
+ AddPattern("Packages.lzma");
+ AddPattern("Sources");
+ AddPattern("Sources.gz");
+ AddPattern("Sources.bz2");
+ AddPattern("Sources.lzma");
+ AddPattern("Release");
+ AddPattern("md5sum.txt");
+
+ Output = stdout;
+ time_t now = time(NULL);
+ char datestr[128];
+ if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
+ gmtime(&now)) == 0)
+ {
+ datestr[0] = '\0';
+ }
+
+ map<string,string> Fields;
+ Fields["Origin"] = "";
+ Fields["Label"] = "";
+ Fields["Suite"] = "";
+ Fields["Version"] = "";
+ Fields["Codename"] = "";
+ Fields["Date"] = datestr;
+ Fields["Architectures"] = "";
+ Fields["Components"] = "";
+ Fields["Description"] = "";
+
+ for(map<string,string>::const_iterator I = Fields.begin();
+ I != Fields.end();
+ ++I)
+ {
+ string Config = string("APT::FTPArchive::Release::") + (*I).first;
+ string Value = _config->Find(Config, (*I).second.c_str());
+ if (Value == "")
+ continue;
+
+ fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
+ }
+}
/*}}}*/
+// ReleaseWriter::DoPackage - Process a single package /*{{{*/
+// ---------------------------------------------------------------------
+bool ReleaseWriter::DoPackage(string FileName)
+{
+ // Strip the DirStrip prefix from the FileName and add the PathPrefix
+ string NewFileName;
+ if (DirStrip.empty() == false &&
+ FileName.length() > DirStrip.length() &&
+ stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
+ DirStrip.begin(),DirStrip.end()) == 0)
+ {
+ NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
+ while (NewFileName[0] == '/')
+ NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
+ }
+ else
+ NewFileName = FileName;
+
+ if (PathPrefix.empty() == false)
+ NewFileName = flCombine(PathPrefix,NewFileName);
+
+ FileFd fd(FileName, FileFd::ReadOnly);
+
+ if (!fd.IsOpen())
+ {
+ return false;
+ }
+
+ CheckSums[NewFileName].size = fd.Size();
+
+ MD5Summation MD5;
+ MD5.AddFD(fd.Fd(), fd.Size());
+ CheckSums[NewFileName].MD5 = MD5.Result();
+
+ fd.Seek(0);
+ SHA1Summation SHA1;
+ SHA1.AddFD(fd.Fd(), fd.Size());
+ CheckSums[NewFileName].SHA1 = SHA1.Result();
+
+ fd.Seek(0);
+ SHA256Summation SHA256;
+ SHA256.AddFD(fd.Fd(), fd.Size());
+ CheckSums[NewFileName].SHA256 = SHA256.Result();
+
+ fd.Close();
+
+ return true;
+}
+
+ /*}}}*/
+// ReleaseWriter::Finish - Output the checksums /*{{{*/
+// ---------------------------------------------------------------------
+void ReleaseWriter::Finish()
+{
+ fprintf(Output, "MD5Sum:\n");
+ for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
+ I != CheckSums.end();
+ ++I)
+ {
+ fprintf(Output, " %s %16ld %s\n",
+ (*I).second.MD5.c_str(),
+ (*I).second.size,
+ (*I).first.c_str());
+ }
+
+ fprintf(Output, "SHA1:\n");
+ for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
+ I != CheckSums.end();
+ ++I)
+ {
+ fprintf(Output, " %s %16ld %s\n",
+ (*I).second.SHA1.c_str(),
+ (*I).second.size,
+ (*I).first.c_str());
+ }
+
+ fprintf(Output, "SHA256:\n");
+ for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
+ I != CheckSums.end();
+ ++I)
+ {
+ fprintf(Output, " %s %16ld %s\n",
+ (*I).second.SHA256.c_str(),
+ (*I).second.size,
+ (*I).first.c_str());
+ }
+}
+