]> git.saurik.com Git - apt.git/blobdiff - ftparchive/writer.cc
* Translations:
[apt.git] / ftparchive / writer.cc
index b3ee49c758538e5abd55f7aba877148c31ab24f0..9d248ab86f2a7f30878448a513fc486e904b1ba2 100644 (file)
@@ -1,6 +1,6 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: writer.cc,v 1.5 2002/04/24 05:02:40 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;
 
@@ -57,8 +58,6 @@ FTWScanner::FTWScanner()
 {
    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)
@@ -69,31 +68,42 @@ FTWScanner::FTWScanner()
 // ---------------------------------------------------------------------
 /* 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
@@ -101,7 +111,8 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
       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
@@ -118,16 +129,16 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
         
         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;
    }
    
@@ -144,19 +155,19 @@ bool FTWScanner::RecursiveScan(string Dir)
    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;
    }
    
@@ -174,14 +185,14 @@ bool FTWScanner::LoadFileList(string Dir,string File)
    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
@@ -205,12 +216,14 @@ bool FTWScanner::LoadFileList(string Dir,string File)
         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;
    }
   
@@ -223,7 +236,7 @@ bool FTWScanner::LoadFileList(string Dir,string File)
 /* */
 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 &&
@@ -238,25 +251,26 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
            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);
                  }            
@@ -264,9 +278,9 @@ bool FTWScanner::Delink(string &FileName,const char *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;
@@ -275,31 +289,23 @@ bool FTWScanner::Delink(string &FileName,const char *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);
 
@@ -317,60 +323,85 @@ PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides) :
 
    _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;
@@ -390,7 +421,9 @@ bool PackagesWriter::DoPackage(string FileName)
 
    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);
@@ -404,9 +437,8 @@ bool PackagesWriter::DoPackage(string FileName)
       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());
       }      
    }
    
@@ -448,8 +480,7 @@ SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
                             string ExtOverrides)
 {
    Output = stdout;
-   Ext[0] = ".dsc";
-   Ext[1] = 0;
+   AddPattern("*.dsc");
    DeLinkLimit = 0;
    Buffer = 0;
    BufSize = 0;
@@ -463,6 +494,10 @@ SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
    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);
    
@@ -535,10 +570,10 @@ bool SourcesWriter::DoPackage(string FileName)
       
    // 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());
       
@@ -550,11 +585,9 @@ bool SourcesWriter::DoPackage(string FileName)
       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)
@@ -562,28 +595,36 @@ bool SourcesWriter::DoPackage(string FileName)
            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
@@ -625,7 +666,7 @@ bool SourcesWriter::DoPackage(string FileName)
          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;
       }
    }
@@ -654,9 +695,8 @@ bool SourcesWriter::DoPackage(string FileName)
       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)
@@ -686,8 +726,7 @@ ContentsWriter::ContentsWriter(string DB) :
                    Db(DB), Stats(Db.Stats)
 
 {
-   Ext[0] = ".deb";
-   Ext[1] = 0;
+   AddPattern("*.deb");
    Output = stdout;
 }
                                                                        /*}}}*/
@@ -697,26 +736,14 @@ ContentsWriter::ContentsWriter(string DB) :
    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");
    }
 
@@ -736,7 +763,7 @@ bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
    
    // Open the package file
    int CompFd = -1;
-   int Proc = -1;
+   pid_t Proc = -1;
    if (Pkgs.OpenOld(CompFd,Proc) == false)
       return false;
    
@@ -777,4 +804,141 @@ bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
    
    return true;
 }
+
+                                                                       /*}}}*/
+
+// ReleaseWriter::ReleaseWriter - Constructor                          /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ReleaseWriter::ReleaseWriter(string DB)
+{
+   AddPattern("Packages");
+   AddPattern("Packages.gz");
+   AddPattern("Packages.bz2");
+   AddPattern("Sources");
+   AddPattern("Sources.gz");
+   AddPattern("Sources.bz2");
+   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());
+   }
+}
+