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 inline void SetTFRewriteData(struct TFRewriteData
&tfrd
,
60 const char *newtag
= 0)
63 tfrd
.Rewrite
= rewrite
;
68 // FTWScanner::FTWScanner - Constructor /*{{{*/
69 // ---------------------------------------------------------------------
71 FTWScanner::FTWScanner(string
const &Arch
): Arch(Arch
)
74 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
76 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
77 DoSHA1
= _config
->FindB("APT::FTPArchive::SHA1",true);
78 DoSHA256
= _config
->FindB("APT::FTPArchive::SHA256",true);
79 DoSHA512
= _config
->FindB("APT::FTPArchive::SHA512",true);
82 // FTWScanner::Scanner - FTW Scanner /*{{{*/
83 // ---------------------------------------------------------------------
84 /* This is the FTW scanner, it processes each directory element in the
86 int FTWScanner::ScannerFTW(const char *File
,const struct stat
* /*sb*/,int Flag
)
91 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
96 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
101 return ScannerFile(File
, true);
104 // FTWScanner::ScannerFile - File Scanner /*{{{*/
105 // ---------------------------------------------------------------------
107 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
109 const char *LastComponent
= strrchr(File
, '/');
110 char *RealPath
= NULL
;
112 if (LastComponent
== NULL
)
113 LastComponent
= File
;
117 vector
<string
>::const_iterator I
;
118 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
120 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
123 if (I
== Owner
->Patterns
.end())
126 /* Process it. If the file is a link then resolve it into an absolute
127 name.. This works best if the directory components the scanner are
128 given are not links themselves. */
130 Owner
->OriginalPath
= File
;
132 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
133 (RealPath
= realpath(File
,NULL
)) != 0)
135 Owner
->DoPackage(RealPath
);
139 Owner
->DoPackage(File
);
141 if (_error
->empty() == false)
143 // Print any errors or warnings found
145 bool SeenPath
= false;
146 while (_error
->empty() == false)
150 bool const Type
= _error
->PopMessage(Err
);
152 cerr
<< _("E: ") << Err
<< endl
;
154 cerr
<< _("W: ") << Err
<< endl
;
156 if (Err
.find(File
) != string::npos
)
160 if (SeenPath
== false)
161 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
168 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
169 // ---------------------------------------------------------------------
171 bool FTWScanner::RecursiveScan(string
const &Dir
)
173 char *RealPath
= NULL
;
174 /* If noprefix is set then jam the scan root in, so we don't generate
175 link followed paths out of control */
176 if (InternalPrefix
.empty() == true)
178 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
179 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
180 InternalPrefix
= RealPath
;
184 // Do recursive directory searching
186 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
188 // Error treewalking?
191 if (_error
->PendingError() == false)
192 _error
->Errno("ftw",_("Tree walking failed"));
199 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
200 // ---------------------------------------------------------------------
201 /* This is an alternative to using FTW to locate files, it reads the list
202 of files from another file. */
203 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
205 char *RealPath
= NULL
;
206 /* If noprefix is set then jam the scan root in, so we don't generate
207 link followed paths out of control */
208 if (InternalPrefix
.empty() == true)
210 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
211 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
212 InternalPrefix
= RealPath
;
217 FILE *List
= fopen(File
.c_str(),"r");
219 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
221 /* We are a tad tricky here.. We prefix the buffer with the directory
222 name, that way if we need a full path with just use line.. Sneaky and
226 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
227 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
229 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
230 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
232 char *FileName
= _strstrip(FileStart
);
233 if (FileName
[0] == 0)
236 if (FileName
[0] != '/')
238 if (FileName
!= FileStart
)
239 memmove(FileStart
,FileName
,strlen(FileStart
));
246 if (stat(FileName
,&St
) != 0)
250 if (ScannerFile(FileName
, false) != 0)
258 // FTWScanner::Delink - Delink symlinks /*{{{*/
259 // ---------------------------------------------------------------------
261 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
262 unsigned long long &DeLinkBytes
,
263 unsigned long long const &FileSize
)
265 // See if this isn't an internaly prefix'd file name.
266 if (InternalPrefix
.empty() == false &&
267 InternalPrefix
.length() < FileName
.length() &&
268 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
269 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
271 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
273 // Tidy up the display
274 if (DeLinkBytes
== 0)
278 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
279 SizeToStr(FileSize
).c_str());
282 if (NoLinkAct
== false)
285 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
286 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
289 if (unlink(OriginalPath
) != 0)
290 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
293 if (link(FileName
.c_str(),OriginalPath
) != 0)
295 // Panic! Restore the symlink
296 if (symlink(OldLink
,OriginalPath
) != 0)
297 _error
->Errno("symlink", "failed to restore symlink");
298 return _error
->Errno("link",_("*** Failed to link %s to %s"),
306 DeLinkBytes
+= FileSize
;
307 if (DeLinkBytes
/1024 >= DeLinkLimit
)
308 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
311 FileName
= OriginalPath
;
318 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
319 // ---------------------------------------------------------------------
321 PackagesWriter::PackagesWriter(string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
322 string
const &Arch
) :
323 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
326 SetExts(".deb .udeb");
329 // Process the command line options
330 DoMD5
= _config
->FindB("APT::FTPArchive::Packages::MD5",DoMD5
);
331 DoSHA1
= _config
->FindB("APT::FTPArchive::Packages::SHA1",DoSHA1
);
332 DoSHA256
= _config
->FindB("APT::FTPArchive::Packages::SHA256",DoSHA256
);
333 DoSHA512
= _config
->FindB("APT::FTPArchive::Packages::SHA512",DoSHA512
);
334 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
335 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
336 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
337 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
339 if (Db
.Loaded() == false)
342 // Read the override file
343 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
348 if (ExtOverrides
.empty() == false)
349 Over
.ReadExtraOverride(ExtOverrides
);
351 _error
->DumpErrors();
354 // FTWScanner::SetExts - Set extensions to support /*{{{*/
355 // ---------------------------------------------------------------------
357 bool FTWScanner::SetExts(string
const &Vals
)
360 string::size_type Start
= 0;
361 while (Start
<= Vals
.length()-1)
363 string::size_type
const Space
= Vals
.find(' ',Start
);
364 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
365 if ( Arch
.empty() == false )
367 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
368 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
371 AddPattern(string("*") + Vals
.substr(Start
, Length
));
380 // PackagesWriter::DoPackage - Process a single package /*{{{*/
381 // ---------------------------------------------------------------------
382 /* This method takes a package and gets its control information and
383 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
384 rewritten and the path/size/hash appended. */
385 bool PackagesWriter::DoPackage(string FileName
)
387 // Pull all the data we need form the DB
388 if (Db
.GetFileInfo(FileName
,
389 true, /* DoControl */
391 true, /* GenContentsOnly */
392 false, /* DoSource */
393 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
, DoAlwaysStat
) == false)
398 unsigned long long FileSize
= Db
.GetFileSize();
399 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
402 // Lookup the overide information
403 pkgTagSection
&Tags
= Db
.Control
.Section
;
404 string Package
= Tags
.FindS("Package");
406 // if we generate a Packages file for a given arch, we use it to
407 // look for overrides. if we run in "simple" mode without the
408 // "Architecures" variable in the config we use the architecure value
413 Architecture
= Tags
.FindS("Architecture");
414 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
416 if (Package
.empty() == true)
417 return _error
->Error(_("Archive had no package field"));
419 // If we need to do any rewriting of the header do it now..
420 if (OverItem
.get() == 0)
422 if (NoOverride
== false)
425 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
428 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
429 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
430 OverItem
->Priority
= Tags
.FindS("Priority");
434 sprintf(Size
,"%llu", (unsigned long long) FileSize
);
436 // Strip the DirStrip prefix from the FileName and add the PathPrefix
438 if (DirStrip
.empty() == false &&
439 FileName
.length() > DirStrip
.length() &&
440 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
441 DirStrip
.begin(),DirStrip
.end()) == 0)
442 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
444 NewFileName
= FileName
;
445 if (PathPrefix
.empty() == false)
446 NewFileName
= flCombine(PathPrefix
,NewFileName
);
448 /* Configuration says we don't want to include the long Description
449 in the package file - instead we want to ship a separated file */
451 if (LongDescription
== false) {
452 desc
= Tags
.FindS("Description").append("\n");
453 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
456 // This lists all the changes to the fields we are going to make.
457 // (7 hardcoded + maintainer + suggests + end marker)
458 TFRewriteData Changes
[6+2+OverItem
->FieldOverride
.size()+1+1];
460 unsigned int End
= 0;
461 SetTFRewriteData(Changes
[End
++], "Size", Size
);
463 SetTFRewriteData(Changes
[End
++], "MD5sum", Db
.MD5Res
.c_str());
465 SetTFRewriteData(Changes
[End
++], "SHA1", Db
.SHA1Res
.c_str());
466 if (DoSHA256
== true)
467 SetTFRewriteData(Changes
[End
++], "SHA256", Db
.SHA256Res
.c_str());
468 if (DoSHA512
== true)
469 SetTFRewriteData(Changes
[End
++], "SHA512", Db
.SHA512Res
.c_str());
470 SetTFRewriteData(Changes
[End
++], "Filename", NewFileName
.c_str());
471 SetTFRewriteData(Changes
[End
++], "Priority", OverItem
->Priority
.c_str());
472 SetTFRewriteData(Changes
[End
++], "Status", 0);
473 SetTFRewriteData(Changes
[End
++], "Optional", 0);
475 string DescriptionMd5
;
476 if (LongDescription
== false) {
477 MD5Summation descmd5
;
478 descmd5
.Add(desc
.c_str());
479 DescriptionMd5
= descmd5
.Result().Value();
480 SetTFRewriteData(Changes
[End
++], "Description-md5", DescriptionMd5
.c_str());
481 if (TransWriter
!= NULL
)
482 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
485 // Rewrite the maintainer field if necessary
487 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
488 if (MaintFailed
== true)
490 if (NoOverride
== false)
493 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
494 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
498 if (NewMaint
.empty() == false)
499 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
501 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
502 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
503 but dpkg does this append bit. So we do the append bit, at least that way the
504 status file and package file will remain similar. There are other transforms
505 but optional is the only legacy one still in use for some lazy reason. */
506 string OptionalStr
= Tags
.FindS("Optional");
507 if (OptionalStr
.empty() == false)
509 if (Tags
.FindS("Suggests").empty() == false)
510 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
511 SetTFRewriteData(Changes
[End
++], "Suggests", OptionalStr
.c_str());
514 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
515 I
!= OverItem
->FieldOverride
.end(); ++I
)
516 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
518 SetTFRewriteData(Changes
[End
++], 0, 0);
520 // Rewrite and store the fields.
521 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
) == false)
523 fprintf(Output
,"\n");
529 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
530 // ---------------------------------------------------------------------
531 /* Create a Translation-Master file for this Packages file */
532 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
533 mode_t
const &Permissions
) : Output(NULL
),
536 if (File
.empty() == true)
539 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
540 Output
= Comp
->Input
;
543 // TranslationWriter::DoPackage - Process a single package /*{{{*/
544 // ---------------------------------------------------------------------
545 /* Create a Translation-Master file for this Packages file */
546 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
552 // Different archs can include different versions and therefore
553 // different descriptions - so we need to check for both name and md5.
554 string
const Record
= Pkg
+ ":" + MD5
;
556 if (Included
.find(Record
) != Included
.end())
559 fprintf(Output
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
560 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
562 Included
.insert(Record
);
566 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
567 // ---------------------------------------------------------------------
569 TranslationWriter::~TranslationWriter()
578 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
579 // ---------------------------------------------------------------------
581 SourcesWriter::SourcesWriter(string
const &DB
, string
const &BOverrides
,string
const &SOverrides
,
582 string
const &ExtOverrides
) :
583 Db(DB
), Stats(Db
.Stats
)
591 // Process the command line options
592 DoMD5
= _config
->FindB("APT::FTPArchive::Sources::MD5",DoMD5
);
593 DoSHA1
= _config
->FindB("APT::FTPArchive::Sources::SHA1",DoSHA1
);
594 DoSHA256
= _config
->FindB("APT::FTPArchive::Sources::SHA256",DoSHA256
);
595 DoSHA512
= _config
->FindB("APT::FTPArchive::Sources::SHA512",DoSHA512
);
596 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
597 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
599 // Read the override file
600 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
605 // WTF?? The logic above: if we can't read binary overrides, don't even try
606 // reading source overrides. if we can read binary overrides, then say there
607 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
609 if (ExtOverrides
.empty() == false)
610 SOver
.ReadExtraOverride(ExtOverrides
);
612 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
613 SOver
.ReadOverride(SOverrides
,true);
616 // SourcesWriter::DoPackage - Process a single package /*{{{*/
617 // ---------------------------------------------------------------------
619 bool SourcesWriter::DoPackage(string FileName
)
621 // Pull all the data we need form the DB
622 if (Db
.GetFileInfo(FileName
,
623 false, /* DoControl */
624 false, /* DoContents */
625 false, /* GenContentsOnly */
627 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
, DoAlwaysStat
) == false)
632 // we need to perform a "write" here (this is what finish is doing)
633 // because the call to Db.GetFileInfo() in the loop will change
638 if (Tags
.Scan(Db
.Dsc
.Data
.c_str(), Db
.Dsc
.Data
.length()) == false)
639 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
641 if (Tags
.Exists("Source") == false)
642 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
645 // Lookup the overide information, finding first the best priority.
647 string Bins
= Tags
.FindS("Binary");
648 char Buffer
[Bins
.length() + 1];
649 auto_ptr
<Override::Item
> OverItem(0);
650 if (Bins
.empty() == false)
652 strcpy(Buffer
,Bins
.c_str());
654 // Ignore too-long errors.
656 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
658 // Look at all the binaries
659 unsigned char BestPrioV
= pkgCache::State::Extra
;
660 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
662 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
666 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
667 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
669 BestPrioV
= NewPrioV
;
670 BestPrio
= Itm
->Priority
;
673 if (OverItem
.get() == 0)
678 // If we need to do any rewriting of the header do it now..
679 if (OverItem
.get() == 0)
681 if (NoOverride
== false)
684 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
687 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
691 if (stat(FileName
.c_str(), &St
) != 0)
692 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
694 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
695 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
696 if (SOverItem
.get() == 0)
698 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
699 SOverItem
= auto_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
700 if (SOverItem
.get() == 0)
702 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
703 SOverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
704 *SOverItem
= *OverItem
;
708 // Add the dsc to the files hash list
709 string
const strippedName
= flNotDir(FileName
);
710 std::ostringstream ostreamFiles
;
711 if (DoMD5
== true && Tags
.Exists("Files"))
712 ostreamFiles
<< "\n " << Db
.MD5Res
.c_str() << " " << St
.st_size
<< " "
713 << strippedName
<< "\n " << Tags
.FindS("Files");
714 string
const Files
= ostreamFiles
.str();
716 std::ostringstream ostreamSha1
;
717 if (DoSHA1
== true && Tags
.Exists("Checksums-Sha1"))
718 ostreamSha1
<< "\n " << string(Db
.SHA1Res
.c_str()) << " " << St
.st_size
<< " "
719 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha1");
721 std::ostringstream ostreamSha256
;
722 if (DoSHA256
== true && Tags
.Exists("Checksums-Sha256"))
723 ostreamSha256
<< "\n " << string(Db
.SHA256Res
.c_str()) << " " << St
.st_size
<< " "
724 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha256");
726 std::ostringstream ostreamSha512
;
727 if (DoSHA512
== true && Tags
.Exists("Checksums-Sha512"))
728 ostreamSha512
<< "\n " << string(Db
.SHA512Res
.c_str()) << " " << St
.st_size
<< " "
729 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha512");
731 // Strip the DirStrip prefix from the FileName and add the PathPrefix
733 if (DirStrip
.empty() == false &&
734 FileName
.length() > DirStrip
.length() &&
735 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
736 NewFileName
= string(OriginalPath
+ DirStrip
.length());
738 NewFileName
= OriginalPath
;
739 if (PathPrefix
.empty() == false)
740 NewFileName
= flCombine(PathPrefix
,NewFileName
);
742 string Directory
= flNotFile(OriginalPath
);
743 string Package
= Tags
.FindS("Source");
745 // Perform operation over all of the files
747 const char *C
= Files
.c_str();
748 char *RealPath
= NULL
;
749 for (;isspace(*C
); C
++);
752 // Parse each of the elements
753 if (ParseQuoteWord(C
,ParseJnk
) == false ||
754 ParseQuoteWord(C
,ParseJnk
) == false ||
755 ParseQuoteWord(C
,ParseJnk
) == false)
756 return _error
->Error("Error parsing file record");
758 string OriginalPath
= Directory
+ ParseJnk
;
760 // Add missing hashes to source files
761 if ((DoSHA1
== true && !Tags
.Exists("Checksums-Sha1")) ||
762 (DoSHA256
== true && !Tags
.Exists("Checksums-Sha256")) ||
763 (DoSHA512
== true && !Tags
.Exists("Checksums-Sha512")))
765 if (Db
.GetFileInfo(OriginalPath
,
766 false, /* DoControl */
767 false, /* DoContents */
768 false, /* GenContentsOnly */
769 false, /* DoSource */
770 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
,
771 DoAlwaysStat
) == false)
773 return _error
->Error("Error getting file info");
776 if (DoSHA1
== true && !Tags
.Exists("Checksums-Sha1"))
777 ostreamSha1
<< "\n " << string(Db
.SHA1Res
) << " "
778 << Db
.GetFileSize() << " " << ParseJnk
;
780 if (DoSHA256
== true && !Tags
.Exists("Checksums-Sha256"))
781 ostreamSha256
<< "\n " << string(Db
.SHA256Res
) << " "
782 << Db
.GetFileSize() << " " << ParseJnk
;
784 if (DoSHA512
== true && !Tags
.Exists("Checksums-Sha512"))
785 ostreamSha512
<< "\n " << string(Db
.SHA512Res
) << " "
786 << Db
.GetFileSize() << " " << ParseJnk
;
788 // write back the GetFileInfo() stats data
792 // Perform the delinking operation
795 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
796 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
798 string RP
= RealPath
;
800 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
805 Directory
= flNotFile(NewFileName
);
806 if (Directory
.length() > 2)
807 Directory
.erase(Directory
.end()-1);
809 string
const ChecksumsSha1
= ostreamSha1
.str();
810 string
const ChecksumsSha256
= ostreamSha256
.str();
811 string
const ChecksumsSha512
= ostreamSha512
.str();
813 // This lists all the changes to the fields we are going to make.
814 // (5 hardcoded + checksums + maintainer + end marker)
815 TFRewriteData Changes
[5+2+1+SOverItem
->FieldOverride
.size()+1];
817 unsigned int End
= 0;
818 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
819 if (Files
.empty() == false)
820 SetTFRewriteData(Changes
[End
++],"Files",Files
.c_str());
821 if (ChecksumsSha1
.empty() == false)
822 SetTFRewriteData(Changes
[End
++],"Checksums-Sha1",ChecksumsSha1
.c_str());
823 if (ChecksumsSha256
.empty() == false)
824 SetTFRewriteData(Changes
[End
++],"Checksums-Sha256",ChecksumsSha256
.c_str());
825 if (ChecksumsSha512
.empty() == false)
826 SetTFRewriteData(Changes
[End
++],"Checksums-Sha512",ChecksumsSha512
.c_str());
827 if (Directory
!= "./")
828 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
829 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
830 SetTFRewriteData(Changes
[End
++],"Status",0);
832 // Rewrite the maintainer field if necessary
834 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
835 if (MaintFailed
== true)
837 if (NoOverride
== false)
840 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
841 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
844 if (NewMaint
.empty() == false)
845 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
847 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
848 I
!= SOverItem
->FieldOverride
.end(); ++I
)
849 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
851 SetTFRewriteData(Changes
[End
++], 0, 0);
853 // Rewrite and store the fields.
854 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
856 fprintf(Output
,"\n");
864 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
865 // ---------------------------------------------------------------------
867 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
868 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
875 // ContentsWriter::DoPackage - Process a single package /*{{{*/
876 // ---------------------------------------------------------------------
877 /* If Package is the empty string the control record will be parsed to
878 determine what the package name is. */
879 bool ContentsWriter::DoPackage(string FileName
, string Package
)
881 if (!Db
.GetFileInfo(FileName
,
882 Package
.empty(), /* DoControl */
883 true, /* DoContents */
884 false, /* GenContentsOnly */
885 false, /* DoSource */
888 false, /* DoSHA256 */
889 false)) /* DoSHA512 */
894 // Parse the package name
895 if (Package
.empty() == true)
897 Package
= Db
.Control
.Section
.FindS("Package");
900 Db
.Contents
.Add(Gen
,Package
);
905 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
906 // ---------------------------------------------------------------------
908 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
910 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
911 if (_error
->PendingError() == true)
914 // Open the package file
916 if (Pkgs
.OpenOld(Fd
) == false)
919 pkgTagFile
Tags(&Fd
);
920 if (_error
->PendingError() == true)
924 pkgTagSection Section
;
925 while (Tags
.Step(Section
) == true)
927 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
928 string Package
= Section
.FindS("Section");
929 if (Package
.empty() == false && Package
.end()[-1] != '/')
932 Package
+= Section
.FindS("Package");
935 Package
+= Section
.FindS("Package");
937 DoPackage(File
,Package
);
938 if (_error
->empty() == false)
940 _error
->Error("Errors apply to file '%s'",File
.c_str());
941 _error
->DumpErrors();
945 // Tidy the compressor
953 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
954 // ---------------------------------------------------------------------
956 ReleaseWriter::ReleaseWriter(string
const &/*DB*/)
958 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
960 AddPattern("Packages");
961 AddPattern("Packages.gz");
962 AddPattern("Packages.bz2");
963 AddPattern("Packages.lzma");
964 AddPattern("Packages.xz");
965 AddPattern("Translation-*");
966 AddPattern("Sources");
967 AddPattern("Sources.gz");
968 AddPattern("Sources.bz2");
969 AddPattern("Sources.lzma");
970 AddPattern("Sources.xz");
971 AddPattern("Release");
972 AddPattern("Contents-*");
974 AddPattern("md5sum.txt");
976 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
979 time_t const now
= time(NULL
);
981 setlocale(LC_TIME
, "C");
984 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
990 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
992 if (now
== validuntil
||
993 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
994 gmtime(&validuntil
)) == 0)
999 setlocale(LC_TIME
, "");
1001 map
<string
,string
> Fields
;
1002 Fields
["Origin"] = "";
1003 Fields
["Label"] = "";
1004 Fields
["Suite"] = "";
1005 Fields
["Version"] = "";
1006 Fields
["Codename"] = "";
1007 Fields
["Date"] = datestr
;
1008 Fields
["Valid-Until"] = validstr
;
1009 Fields
["Architectures"] = "";
1010 Fields
["Components"] = "";
1011 Fields
["Description"] = "";
1013 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1017 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1018 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1022 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
1025 DoMD5
= _config
->FindB("APT::FTPArchive::Release::MD5",DoMD5
);
1026 DoSHA1
= _config
->FindB("APT::FTPArchive::Release::SHA1",DoSHA1
);
1027 DoSHA256
= _config
->FindB("APT::FTPArchive::Release::SHA256",DoSHA256
);
1030 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1031 // ---------------------------------------------------------------------
1032 bool ReleaseWriter::DoPackage(string FileName
)
1034 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1036 if (DirStrip
.empty() == false &&
1037 FileName
.length() > DirStrip
.length() &&
1038 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1039 DirStrip
.begin(),DirStrip
.end()) == 0)
1041 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1042 while (NewFileName
[0] == '/')
1043 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1046 NewFileName
= FileName
;
1048 if (PathPrefix
.empty() == false)
1049 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1051 FileFd
fd(FileName
, FileFd::ReadOnly
);
1058 CheckSums
[NewFileName
].size
= fd
.Size();
1061 hs
.AddFD(fd
, 0, DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
);
1063 CheckSums
[NewFileName
].MD5
= hs
.MD5
.Result();
1065 CheckSums
[NewFileName
].SHA1
= hs
.SHA1
.Result();
1066 if (DoSHA256
== true)
1067 CheckSums
[NewFileName
].SHA256
= hs
.SHA256
.Result();
1068 if (DoSHA512
== true)
1069 CheckSums
[NewFileName
].SHA512
= hs
.SHA512
.Result();
1076 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1077 // ---------------------------------------------------------------------
1078 void ReleaseWriter::Finish()
1082 fprintf(Output
, "MD5Sum:\n");
1083 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1084 I
!= CheckSums
.end(); ++I
)
1086 fprintf(Output
, " %s %16llu %s\n",
1087 (*I
).second
.MD5
.c_str(),
1089 (*I
).first
.c_str());
1094 fprintf(Output
, "SHA1:\n");
1095 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1096 I
!= CheckSums
.end(); ++I
)
1098 fprintf(Output
, " %s %16llu %s\n",
1099 (*I
).second
.SHA1
.c_str(),
1101 (*I
).first
.c_str());
1104 if (DoSHA256
== true)
1106 fprintf(Output
, "SHA256:\n");
1107 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1108 I
!= CheckSums
.end(); ++I
)
1110 fprintf(Output
, " %s %16llu %s\n",
1111 (*I
).second
.SHA256
.c_str(),
1113 (*I
).first
.c_str());
1117 fprintf(Output
, "SHA512:\n");
1118 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1119 I
!= CheckSums
.end();
1122 fprintf(Output
, " %s %16llu %s\n",
1123 (*I
).second
.SHA512
.c_str(),
1125 (*I
).first
.c_str());