1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
4 /* ######################################################################
8 The file writer classes. These write various types of output, sources,
11 ##################################################################### */
13 // Include Files /*{{{*/
16 #include <apt-pkg/configuration.h>
17 #include <apt-pkg/deblistparser.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/gpgv.h>
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>
36 #include <sys/types.h>
44 #include "apt-ftparchive.h"
47 #include "multicompress.h"
52 FTWScanner
*FTWScanner::Owner
;
54 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
55 // ---------------------------------------------------------------------
57 static inline TFRewriteData
SetTFRewriteData(const char *tag
,
59 const char *newtag
= 0)
63 tfrd
.Rewrite
= rewrite
;
68 // ConfigToDoHashes - which hashes to generate /*{{{*/
69 static void SingleConfigToDoHashes(unsigned int &DoHashes
, std::string
const &Conf
, unsigned int const Flag
)
71 if (_config
->FindB(Conf
, true) == true)
76 static void ConfigToDoHashes(unsigned int &DoHashes
, std::string
const &Conf
)
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
);
85 // FTWScanner::FTWScanner - Constructor /*{{{*/
86 // ---------------------------------------------------------------------
88 FTWScanner::FTWScanner(string
const &Arch
): Arch(Arch
), DoHashes(~0)
91 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
92 ConfigToDoHashes(DoHashes
, "APT::FTPArchive");
95 // FTWScanner::Scanner - FTW Scanner /*{{{*/
96 // ---------------------------------------------------------------------
97 /* This is the FTW scanner, it processes each directory element in the
99 int FTWScanner::ScannerFTW(const char *File
,const struct stat
* /*sb*/,int Flag
)
104 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
109 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
114 return ScannerFile(File
, true);
117 // FTWScanner::ScannerFile - File Scanner /*{{{*/
118 // ---------------------------------------------------------------------
120 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
122 const char *LastComponent
= strrchr(File
, '/');
123 char *RealPath
= NULL
;
125 if (LastComponent
== NULL
)
126 LastComponent
= File
;
130 vector
<string
>::const_iterator I
;
131 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
133 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
136 if (I
== Owner
->Patterns
.end())
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. */
143 Owner
->OriginalPath
= File
;
145 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
146 (RealPath
= realpath(File
,NULL
)) != 0)
148 Owner
->DoPackage(RealPath
);
152 Owner
->DoPackage(File
);
154 if (_error
->empty() == false)
156 // Print any errors or warnings found
158 bool SeenPath
= false;
159 while (_error
->empty() == false)
163 bool const Type
= _error
->PopMessage(Err
);
165 cerr
<< _("E: ") << Err
<< endl
;
167 cerr
<< _("W: ") << Err
<< endl
;
169 if (Err
.find(File
) != string::npos
)
173 if (SeenPath
== false)
174 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
181 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
182 // ---------------------------------------------------------------------
184 bool FTWScanner::RecursiveScan(string
const &Dir
)
186 char *RealPath
= NULL
;
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)
191 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
192 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
193 InternalPrefix
= RealPath
;
197 // Do recursive directory searching
199 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
201 // Error treewalking?
204 if (_error
->PendingError() == false)
205 _error
->Errno("ftw",_("Tree walking failed"));
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. */
216 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
218 char *RealPath
= NULL
;
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)
223 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
224 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
225 InternalPrefix
= RealPath
;
230 FILE *List
= fopen(File
.c_str(),"r");
232 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
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
239 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
240 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
242 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
243 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
245 char *FileName
= _strstrip(FileStart
);
246 if (FileName
[0] == 0)
249 if (FileName
[0] != '/')
251 if (FileName
!= FileStart
)
252 memmove(FileStart
,FileName
,strlen(FileStart
));
259 if (stat(FileName
,&St
) != 0)
263 if (ScannerFile(FileName
, false) != 0)
271 // FTWScanner::Delink - Delink symlinks /*{{{*/
272 // ---------------------------------------------------------------------
274 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
275 unsigned long long &DeLinkBytes
,
276 unsigned long long const &FileSize
)
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)
284 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
286 // Tidy up the display
287 if (DeLinkBytes
== 0)
291 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
292 SizeToStr(FileSize
).c_str());
295 if (NoLinkAct
== false)
298 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
299 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
302 if (unlink(OriginalPath
) != 0)
303 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
306 if (link(FileName
.c_str(),OriginalPath
) != 0)
308 // Panic! Restore the symlink
309 if (symlink(OldLink
,OriginalPath
) != 0)
310 _error
->Errno("symlink", "failed to restore symlink");
311 return _error
->Errno("link",_("*** Failed to link %s to %s"),
319 DeLinkBytes
+= FileSize
;
320 if (DeLinkBytes
/1024 >= DeLinkLimit
)
321 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
324 FileName
= OriginalPath
;
331 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
332 // ---------------------------------------------------------------------
334 PackagesWriter::PackagesWriter(string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
335 string
const &Arch
) :
336 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
339 SetExts(".deb .udeb");
342 // Process the command line options
343 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Packages");
344 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
345 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
346 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
347 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
349 if (Db
.Loaded() == false)
352 // Read the override file
353 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
358 if (ExtOverrides
.empty() == false)
359 Over
.ReadExtraOverride(ExtOverrides
);
361 _error
->DumpErrors();
364 // FTWScanner::SetExts - Set extensions to support /*{{{*/
365 // ---------------------------------------------------------------------
367 bool FTWScanner::SetExts(string
const &Vals
)
370 string::size_type Start
= 0;
371 while (Start
<= Vals
.length()-1)
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 )
377 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
378 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
381 AddPattern(string("*") + Vals
.substr(Start
, Length
));
390 // PackagesWriter::DoPackage - Process a single package /*{{{*/
391 // ---------------------------------------------------------------------
392 /* This method takes a package and gets its control information and
393 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
394 rewritten and the path/size/hash appended. */
395 bool PackagesWriter::DoPackage(string FileName
)
397 // Pull all the data we need form the DB
398 if (Db
.GetFileInfo(FileName
,
399 true, /* DoControl */
401 true, /* GenContentsOnly */
402 false, /* DoSource */
403 DoHashes
, DoAlwaysStat
) == false)
408 unsigned long long FileSize
= Db
.GetFileSize();
409 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
412 // Lookup the overide information
413 pkgTagSection
&Tags
= Db
.Control
.Section
;
414 string Package
= Tags
.FindS("Package");
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
423 Architecture
= Tags
.FindS("Architecture");
424 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
426 if (Package
.empty() == true)
427 return _error
->Error(_("Archive had no package field"));
429 // If we need to do any rewriting of the header do it now..
430 if (OverItem
.get() == 0)
432 if (NoOverride
== false)
435 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
438 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
439 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
440 OverItem
->Priority
= Tags
.FindS("Priority");
443 // Strip the DirStrip prefix from the FileName and add the PathPrefix
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());
451 NewFileName
= FileName
;
452 if (PathPrefix
.empty() == false)
453 NewFileName
= flCombine(PathPrefix
,NewFileName
);
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 */
458 if (LongDescription
== false) {
459 desc
= Tags
.FindS("Description").append("\n");
460 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
463 // This lists all the changes to the fields we are going to make.
464 std::vector
<TFRewriteData
> Changes
;
467 strprintf(Size
, "%llu", (unsigned long long) FileSize
);
468 Changes
.push_back(SetTFRewriteData("Size", Size
.c_str()));
470 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
472 if (hs
->HashType() == "MD5Sum")
473 Changes
.push_back(SetTFRewriteData("MD5sum", hs
->HashValue().c_str()));
474 else if (hs
->HashType() == "Checksum-FileSize")
477 Changes
.push_back(SetTFRewriteData(hs
->HashType().c_str(), hs
->HashValue().c_str()));
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));
484 string DescriptionMd5
;
485 if (LongDescription
== false) {
486 MD5Summation descmd5
;
487 descmd5
.Add(desc
.c_str());
488 DescriptionMd5
= descmd5
.Result().Value();
489 Changes
.push_back(SetTFRewriteData("Description-md5", DescriptionMd5
.c_str()));
490 if (TransWriter
!= NULL
)
491 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
494 // Rewrite the maintainer field if necessary
496 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
497 if (MaintFailed
== true)
499 if (NoOverride
== false)
502 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
503 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
507 if (NewMaint
.empty() == false)
508 Changes
.push_back(SetTFRewriteData("Maintainer", NewMaint
.c_str()));
510 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
511 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
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)
518 if (Tags
.FindS("Suggests").empty() == false)
519 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
520 Changes
.push_back(SetTFRewriteData("Suggests", OptionalStr
.c_str()));
523 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
524 I
!= OverItem
->FieldOverride
.end(); ++I
)
525 Changes
.push_back(SetTFRewriteData(I
->first
.c_str(),I
->second
.c_str()));
527 Changes
.push_back(SetTFRewriteData( 0, 0));
529 // Rewrite and store the fields.
530 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
.data()) == false)
532 fprintf(Output
,"\n");
538 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
539 // ---------------------------------------------------------------------
540 /* Create a Translation-Master file for this Packages file */
541 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
542 mode_t
const &Permissions
) : Output(NULL
),
545 if (File
.empty() == true)
548 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
549 Output
= Comp
->Input
;
552 // TranslationWriter::DoPackage - Process a single package /*{{{*/
553 // ---------------------------------------------------------------------
554 /* Create a Translation-Master file for this Packages file */
555 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
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
;
565 if (Included
.find(Record
) != Included
.end())
568 fprintf(Output
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
569 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
571 Included
.insert(Record
);
575 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
576 // ---------------------------------------------------------------------
578 TranslationWriter::~TranslationWriter()
587 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
588 // ---------------------------------------------------------------------
590 SourcesWriter::SourcesWriter(string
const &DB
, string
const &BOverrides
,string
const &SOverrides
,
591 string
const &ExtOverrides
) :
592 Db(DB
), Stats(Db
.Stats
)
600 // Process the command line options
601 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Sources");
602 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
603 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
605 // Read the override file
606 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
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
615 if (ExtOverrides
.empty() == false)
616 SOver
.ReadExtraOverride(ExtOverrides
);
618 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
619 SOver
.ReadOverride(SOverrides
,true);
622 // SourcesWriter::DoPackage - Process a single package /*{{{*/
623 static 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
)
627 if ((DoHashes
& DoIt
) != DoIt
|| Tags
.Exists(FieldName
) == false || Hash
== NULL
)
629 out
<< "\n " << Hash
->HashValue() << " " << Size
<< " " << FileName
630 << "\n " << Tags
.FindS(FieldName
);
633 bool SourcesWriter::DoPackage(string FileName
)
635 // Pull all the data we need form the DB
636 if (Db
.GetFileInfo(FileName
,
637 false, /* DoControl */
638 false, /* DoContents */
639 false, /* GenContentsOnly */
641 DoHashes
, DoAlwaysStat
) == false)
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
652 if (Tags
.Scan(Db
.Dsc
.Data
.c_str(), Db
.Dsc
.Data
.length()) == false)
653 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
655 if (Tags
.Exists("Source") == false)
656 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
659 // Lookup the overide information, finding first the best priority.
661 string Bins
= Tags
.FindS("Binary");
662 char Buffer
[Bins
.length() + 1];
663 auto_ptr
<Override::Item
> OverItem(0);
664 if (Bins
.empty() == false)
666 strcpy(Buffer
,Bins
.c_str());
668 // Ignore too-long errors.
670 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
672 // Look at all the binaries
673 unsigned char BestPrioV
= pkgCache::State::Extra
;
674 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
676 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
680 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
681 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
683 BestPrioV
= NewPrioV
;
684 BestPrio
= Itm
->Priority
;
687 if (OverItem
.get() == 0)
692 // If we need to do any rewriting of the header do it now..
693 if (OverItem
.get() == 0)
695 if (NoOverride
== false)
698 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
701 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
705 if (stat(FileName
.c_str(), &St
) != 0)
706 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
708 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
709 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
710 if (SOverItem
.get() == 0)
712 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
713 SOverItem
= auto_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
714 if (SOverItem
.get() == 0)
716 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
717 SOverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
718 *SOverItem
= *OverItem
;
722 // Add the dsc to the files hash list
723 string
const strippedName
= flNotDir(FileName
);
724 std::ostringstream ostreamFiles
;
725 addDscHash(ostreamFiles
, DoHashes
, Hashes::MD5SUM
, Tags
, "Files", Db
.HashesList
.find("MD5Sum"), St
.st_size
, strippedName
);
726 string
const Files
= ostreamFiles
.str();
728 std::ostringstream ostreamSha1
;
729 addDscHash(ostreamSha1
, DoHashes
, Hashes::SHA1SUM
, Tags
, "Checksums-Sha1", Db
.HashesList
.find("SHA1"), St
.st_size
, strippedName
);
730 std::ostringstream ostreamSha256
;
731 addDscHash(ostreamSha256
, DoHashes
, Hashes::SHA256SUM
, Tags
, "Checksums-Sha256", Db
.HashesList
.find("SHA256"), St
.st_size
, strippedName
);
732 std::ostringstream ostreamSha512
;
733 addDscHash(ostreamSha512
, DoHashes
, Hashes::SHA512SUM
, Tags
, "Checksums-Sha512", Db
.HashesList
.find("SHA512"), St
.st_size
, strippedName
);
735 // Strip the DirStrip prefix from the FileName and add the PathPrefix
737 if (DirStrip
.empty() == false &&
738 FileName
.length() > DirStrip
.length() &&
739 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
740 NewFileName
= string(OriginalPath
+ DirStrip
.length());
742 NewFileName
= OriginalPath
;
743 if (PathPrefix
.empty() == false)
744 NewFileName
= flCombine(PathPrefix
,NewFileName
);
746 string Directory
= flNotFile(OriginalPath
);
747 string Package
= Tags
.FindS("Source");
749 // Perform operation over all of the files
751 const char *C
= Files
.c_str();
752 char *RealPath
= NULL
;
753 for (;isspace(*C
); C
++);
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");
762 string OriginalPath
= Directory
+ ParseJnk
;
764 // Add missing hashes to source files
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")))
769 if (Db
.GetFileInfo(OriginalPath
,
770 false, /* DoControl */
771 false, /* DoContents */
772 false, /* GenContentsOnly */
773 false, /* DoSource */
775 DoAlwaysStat
) == false)
777 return _error
->Error("Error getting file info");
780 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
782 if (hs
->HashType() == "MD5Sum" || hs
->HashType() == "Checksum-FileSize")
784 char const * fieldname
;
786 if (hs
->HashType() == "SHA1")
788 fieldname
= "Checksums-Sha1";
791 else if (hs
->HashType() == "SHA256")
793 fieldname
= "Checksums-Sha256";
794 out
= &ostreamSha256
;
796 else if (hs
->HashType() == "SHA512")
798 fieldname
= "Checksums-Sha512";
799 out
= &ostreamSha512
;
803 _error
->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs
->HashType().c_str());
806 if (Tags
.Exists(fieldname
) == true)
808 (*out
) << "\n " << hs
->HashValue() << " " << Db
.GetFileSize() << " " << ParseJnk
;
811 // write back the GetFileInfo() stats data
815 // Perform the delinking operation
818 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
819 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
821 string RP
= RealPath
;
823 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
828 Directory
= flNotFile(NewFileName
);
829 if (Directory
.length() > 2)
830 Directory
.erase(Directory
.end()-1);
832 string
const ChecksumsSha1
= ostreamSha1
.str();
833 string
const ChecksumsSha256
= ostreamSha256
.str();
834 string
const ChecksumsSha512
= ostreamSha512
.str();
836 // This lists all the changes to the fields we are going to make.
837 // (5 hardcoded + checksums + maintainer + end marker)
838 std::vector
<TFRewriteData
> Changes
;
840 Changes
.push_back(SetTFRewriteData("Source", 0));
841 Changes
.push_back(SetTFRewriteData("Package",Package
.c_str()));
842 if (Files
.empty() == false)
843 Changes
.push_back(SetTFRewriteData("Files",Files
.c_str()));
844 if (ChecksumsSha1
.empty() == false)
845 Changes
.push_back(SetTFRewriteData("Checksums-Sha1",ChecksumsSha1
.c_str()));
846 if (ChecksumsSha256
.empty() == false)
847 Changes
.push_back(SetTFRewriteData("Checksums-Sha256",ChecksumsSha256
.c_str()));
848 if (ChecksumsSha512
.empty() == false)
849 Changes
.push_back(SetTFRewriteData("Checksums-Sha512",ChecksumsSha512
.c_str()));
850 if (Directory
!= "./")
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));
855 // Rewrite the maintainer field if necessary
857 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
858 if (MaintFailed
== true)
860 if (NoOverride
== false)
863 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
864 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
867 if (NewMaint
.empty() == false)
868 Changes
.push_back(SetTFRewriteData("Maintainer", NewMaint
.c_str()));
870 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
871 I
!= SOverItem
->FieldOverride
.end(); ++I
)
872 Changes
.push_back(SetTFRewriteData(I
->first
.c_str(),I
->second
.c_str()));
874 Changes
.push_back(SetTFRewriteData(0, 0));
876 // Rewrite and store the fields.
877 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
.data()) == false)
879 fprintf(Output
,"\n");
887 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
888 // ---------------------------------------------------------------------
890 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
891 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
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. */
902 bool ContentsWriter::DoPackage(string FileName
, string Package
)
904 if (!Db
.GetFileInfo(FileName
,
905 Package
.empty(), /* DoControl */
906 true, /* DoContents */
907 false, /* GenContentsOnly */
908 false, /* DoSource */
910 false /* checkMtime */))
915 // Parse the package name
916 if (Package
.empty() == true)
918 Package
= Db
.Control
.Section
.FindS("Package");
921 Db
.Contents
.Add(Gen
,Package
);
926 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
927 // ---------------------------------------------------------------------
929 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
931 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
932 if (_error
->PendingError() == true)
935 // Open the package file
937 if (Pkgs
.OpenOld(Fd
) == false)
940 pkgTagFile
Tags(&Fd
);
941 if (_error
->PendingError() == true)
945 pkgTagSection Section
;
946 while (Tags
.Step(Section
) == true)
948 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
949 string Package
= Section
.FindS("Section");
950 if (Package
.empty() == false && Package
.end()[-1] != '/')
953 Package
+= Section
.FindS("Package");
956 Package
+= Section
.FindS("Package");
958 DoPackage(File
,Package
);
959 if (_error
->empty() == false)
961 _error
->Error("Errors apply to file '%s'",File
.c_str());
962 _error
->DumpErrors();
966 // Tidy the compressor
974 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
975 // ---------------------------------------------------------------------
977 ReleaseWriter::ReleaseWriter(string
const &/*DB*/)
979 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
981 AddPattern("Packages");
982 AddPattern("Packages.gz");
983 AddPattern("Packages.bz2");
984 AddPattern("Packages.lzma");
985 AddPattern("Packages.xz");
986 AddPattern("Translation-*");
987 AddPattern("Sources");
988 AddPattern("Sources.gz");
989 AddPattern("Sources.bz2");
990 AddPattern("Sources.lzma");
991 AddPattern("Sources.xz");
992 AddPattern("Release");
993 AddPattern("Contents-*");
995 AddPattern("md5sum.txt");
997 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
1000 time_t const now
= time(NULL
);
1002 setlocale(LC_TIME
, "C");
1005 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
1011 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
1013 if (now
== validuntil
||
1014 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
1015 gmtime(&validuntil
)) == 0)
1020 setlocale(LC_TIME
, "");
1022 map
<string
,string
> Fields
;
1023 Fields
["Origin"] = "";
1024 Fields
["Label"] = "";
1025 Fields
["Suite"] = "";
1026 Fields
["Version"] = "";
1027 Fields
["Codename"] = "";
1028 Fields
["Date"] = datestr
;
1029 Fields
["Valid-Until"] = validstr
;
1030 Fields
["Architectures"] = "";
1031 Fields
["Components"] = "";
1032 Fields
["Description"] = "";
1034 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1038 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1039 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1043 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
1046 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Release");
1049 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1050 // ---------------------------------------------------------------------
1051 bool ReleaseWriter::DoPackage(string FileName
)
1053 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1055 if (DirStrip
.empty() == false &&
1056 FileName
.length() > DirStrip
.length() &&
1057 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1058 DirStrip
.begin(),DirStrip
.end()) == 0)
1060 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1061 while (NewFileName
[0] == '/')
1062 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1065 NewFileName
= FileName
;
1067 if (PathPrefix
.empty() == false)
1068 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1070 FileFd
fd(FileName
, FileFd::ReadOnly
);
1077 CheckSums
[NewFileName
].size
= fd
.Size();
1079 Hashes
hs(DoHashes
);
1081 CheckSums
[NewFileName
].Hashes
= hs
.GetHashStringList();
1088 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1089 // ---------------------------------------------------------------------
1090 static void printChecksumTypeRecord(FILE * const Output
, char const * const Type
, map
<string
, ReleaseWriter::CheckSum
> const &CheckSums
)
1092 fprintf(Output
, "%s:\n", Type
);
1093 for(map
<string
,ReleaseWriter::CheckSum
>::const_iterator I
= CheckSums
.begin();
1094 I
!= CheckSums
.end(); ++I
)
1096 HashString
const * const hs
= I
->second
.Hashes
.find(Type
);
1099 fprintf(Output
, " %s %16llu %s\n",
1100 hs
->HashValue().c_str(),
1102 (*I
).first
.c_str());
1105 void ReleaseWriter::Finish()
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
);