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 char *Start
= Db
.Dsc
.Data
;
639 char *BlkEnd
= Db
.Dsc
.Data
+ Db
.Dsc
.Length
;
641 // Add extra \n to the end, just in case (as in clearsigned they are missing)
646 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
647 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
649 if (Tags
.Exists("Source") == false)
650 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
653 // Lookup the overide information, finding first the best priority.
655 string Bins
= Tags
.FindS("Binary");
656 char Buffer
[Bins
.length() + 1];
657 auto_ptr
<Override::Item
> OverItem(0);
658 if (Bins
.empty() == false)
660 strcpy(Buffer
,Bins
.c_str());
662 // Ignore too-long errors.
664 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
666 // Look at all the binaries
667 unsigned char BestPrioV
= pkgCache::State::Extra
;
668 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
670 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
674 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
675 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
677 BestPrioV
= NewPrioV
;
678 BestPrio
= Itm
->Priority
;
681 if (OverItem
.get() == 0)
686 // If we need to do any rewriting of the header do it now..
687 if (OverItem
.get() == 0)
689 if (NoOverride
== false)
692 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
695 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
699 if (stat(FileName
.c_str(), &St
) != 0)
700 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
702 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
703 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
704 if (SOverItem
.get() == 0)
706 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
707 SOverItem
= auto_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
708 if (SOverItem
.get() == 0)
710 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
711 SOverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
712 *SOverItem
= *OverItem
;
716 // Add the dsc to the files hash list
717 string
const strippedName
= flNotDir(FileName
);
718 std::ostringstream ostreamFiles
;
719 if (DoMD5
== true && Tags
.Exists("Files"))
720 ostreamFiles
<< "\n " << Db
.MD5Res
.c_str() << " " << St
.st_size
<< " "
721 << strippedName
<< "\n " << Tags
.FindS("Files");
722 string
const Files
= ostreamFiles
.str();
724 std::ostringstream ostreamSha1
;
725 if (DoSHA1
== true && Tags
.Exists("Checksums-Sha1"))
726 ostreamSha1
<< "\n " << string(Db
.SHA1Res
.c_str()) << " " << St
.st_size
<< " "
727 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha1");
729 std::ostringstream ostreamSha256
;
730 if (DoSHA256
== true && Tags
.Exists("Checksums-Sha256"))
731 ostreamSha256
<< "\n " << string(Db
.SHA256Res
.c_str()) << " " << St
.st_size
<< " "
732 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha256");
734 std::ostringstream ostreamSha512
;
735 if (DoSHA512
== true && Tags
.Exists("Checksums-Sha512"))
736 ostreamSha512
<< "\n " << string(Db
.SHA512Res
.c_str()) << " " << St
.st_size
<< " "
737 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha512");
739 // Strip the DirStrip prefix from the FileName and add the PathPrefix
741 if (DirStrip
.empty() == false &&
742 FileName
.length() > DirStrip
.length() &&
743 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
744 NewFileName
= string(OriginalPath
+ DirStrip
.length());
746 NewFileName
= OriginalPath
;
747 if (PathPrefix
.empty() == false)
748 NewFileName
= flCombine(PathPrefix
,NewFileName
);
750 string Directory
= flNotFile(OriginalPath
);
751 string Package
= Tags
.FindS("Source");
753 // Perform operation over all of the files
755 const char *C
= Files
.c_str();
756 char *RealPath
= NULL
;
757 for (;isspace(*C
); C
++);
760 // Parse each of the elements
761 if (ParseQuoteWord(C
,ParseJnk
) == false ||
762 ParseQuoteWord(C
,ParseJnk
) == false ||
763 ParseQuoteWord(C
,ParseJnk
) == false)
764 return _error
->Error("Error parsing file record");
766 string OriginalPath
= Directory
+ ParseJnk
;
768 // Add missing hashes to source files
769 if ((DoSHA1
== true && !Tags
.Exists("Checksums-Sha1")) ||
770 (DoSHA256
== true && !Tags
.Exists("Checksums-Sha256")) ||
771 (DoSHA512
== true && !Tags
.Exists("Checksums-Sha512")))
773 if (Db
.GetFileInfo(OriginalPath
,
774 false, /* DoControl */
775 false, /* DoContents */
776 false, /* GenContentsOnly */
777 false, /* DoSource */
778 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
,
779 DoAlwaysStat
) == false)
781 return _error
->Error("Error getting file info");
784 if (DoSHA1
== true && !Tags
.Exists("Checksums-Sha1"))
785 ostreamSha1
<< "\n " << string(Db
.SHA1Res
) << " "
786 << Db
.GetFileSize() << " " << ParseJnk
;
788 if (DoSHA256
== true && !Tags
.Exists("Checksums-Sha256"))
789 ostreamSha256
<< "\n " << string(Db
.SHA256Res
) << " "
790 << Db
.GetFileSize() << " " << ParseJnk
;
792 if (DoSHA512
== true && !Tags
.Exists("Checksums-Sha512"))
793 ostreamSha512
<< "\n " << string(Db
.SHA512Res
) << " "
794 << Db
.GetFileSize() << " " << ParseJnk
;
796 // write back the GetFileInfo() stats data
800 // Perform the delinking operation
803 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
804 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
806 string RP
= RealPath
;
808 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
813 Directory
= flNotFile(NewFileName
);
814 if (Directory
.length() > 2)
815 Directory
.erase(Directory
.end()-1);
817 string
const ChecksumsSha1
= ostreamSha1
.str();
818 string
const ChecksumsSha256
= ostreamSha256
.str();
819 string
const ChecksumsSha512
= ostreamSha512
.str();
821 // This lists all the changes to the fields we are going to make.
822 // (5 hardcoded + checksums + maintainer + end marker)
823 TFRewriteData Changes
[5+2+1+SOverItem
->FieldOverride
.size()+1];
825 unsigned int End
= 0;
826 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
827 if (Files
.empty() == false)
828 SetTFRewriteData(Changes
[End
++],"Files",Files
.c_str());
829 if (ChecksumsSha1
.empty() == false)
830 SetTFRewriteData(Changes
[End
++],"Checksums-Sha1",ChecksumsSha1
.c_str());
831 if (ChecksumsSha256
.empty() == false)
832 SetTFRewriteData(Changes
[End
++],"Checksums-Sha256",ChecksumsSha256
.c_str());
833 if (ChecksumsSha512
.empty() == false)
834 SetTFRewriteData(Changes
[End
++],"Checksums-Sha512",ChecksumsSha512
.c_str());
835 if (Directory
!= "./")
836 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
837 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
838 SetTFRewriteData(Changes
[End
++],"Status",0);
840 // Rewrite the maintainer field if necessary
842 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
843 if (MaintFailed
== true)
845 if (NoOverride
== false)
848 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
849 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
852 if (NewMaint
.empty() == false)
853 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
855 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
856 I
!= SOverItem
->FieldOverride
.end(); ++I
)
857 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
859 SetTFRewriteData(Changes
[End
++], 0, 0);
861 // Rewrite and store the fields.
862 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
864 fprintf(Output
,"\n");
872 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
873 // ---------------------------------------------------------------------
875 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
876 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
883 // ContentsWriter::DoPackage - Process a single package /*{{{*/
884 // ---------------------------------------------------------------------
885 /* If Package is the empty string the control record will be parsed to
886 determine what the package name is. */
887 bool ContentsWriter::DoPackage(string FileName
, string Package
)
889 if (!Db
.GetFileInfo(FileName
,
890 Package
.empty(), /* DoControl */
891 true, /* DoContents */
892 false, /* GenContentsOnly */
893 false, /* DoSource */
896 false, /* DoSHA256 */
897 false)) /* DoSHA512 */
902 // Parse the package name
903 if (Package
.empty() == true)
905 Package
= Db
.Control
.Section
.FindS("Package");
908 Db
.Contents
.Add(Gen
,Package
);
913 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
914 // ---------------------------------------------------------------------
916 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
918 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
919 if (_error
->PendingError() == true)
922 // Open the package file
924 if (Pkgs
.OpenOld(Fd
) == false)
927 pkgTagFile
Tags(&Fd
);
928 if (_error
->PendingError() == true)
932 pkgTagSection Section
;
933 while (Tags
.Step(Section
) == true)
935 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
936 string Package
= Section
.FindS("Section");
937 if (Package
.empty() == false && Package
.end()[-1] != '/')
940 Package
+= Section
.FindS("Package");
943 Package
+= Section
.FindS("Package");
945 DoPackage(File
,Package
);
946 if (_error
->empty() == false)
948 _error
->Error("Errors apply to file '%s'",File
.c_str());
949 _error
->DumpErrors();
953 // Tidy the compressor
961 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
962 // ---------------------------------------------------------------------
964 ReleaseWriter::ReleaseWriter(string
const &/*DB*/)
966 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
968 AddPattern("Packages");
969 AddPattern("Packages.gz");
970 AddPattern("Packages.bz2");
971 AddPattern("Packages.lzma");
972 AddPattern("Packages.xz");
973 AddPattern("Translation-*");
974 AddPattern("Sources");
975 AddPattern("Sources.gz");
976 AddPattern("Sources.bz2");
977 AddPattern("Sources.lzma");
978 AddPattern("Sources.xz");
979 AddPattern("Release");
980 AddPattern("Contents-*");
982 AddPattern("md5sum.txt");
984 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
987 time_t const now
= time(NULL
);
989 setlocale(LC_TIME
, "C");
992 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
998 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
1000 if (now
== validuntil
||
1001 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
1002 gmtime(&validuntil
)) == 0)
1007 setlocale(LC_TIME
, "");
1009 map
<string
,string
> Fields
;
1010 Fields
["Origin"] = "";
1011 Fields
["Label"] = "";
1012 Fields
["Suite"] = "";
1013 Fields
["Version"] = "";
1014 Fields
["Codename"] = "";
1015 Fields
["Date"] = datestr
;
1016 Fields
["Valid-Until"] = validstr
;
1017 Fields
["Architectures"] = "";
1018 Fields
["Components"] = "";
1019 Fields
["Description"] = "";
1021 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1025 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1026 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1030 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
1033 DoMD5
= _config
->FindB("APT::FTPArchive::Release::MD5",DoMD5
);
1034 DoSHA1
= _config
->FindB("APT::FTPArchive::Release::SHA1",DoSHA1
);
1035 DoSHA256
= _config
->FindB("APT::FTPArchive::Release::SHA256",DoSHA256
);
1038 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1039 // ---------------------------------------------------------------------
1040 bool ReleaseWriter::DoPackage(string FileName
)
1042 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1044 if (DirStrip
.empty() == false &&
1045 FileName
.length() > DirStrip
.length() &&
1046 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1047 DirStrip
.begin(),DirStrip
.end()) == 0)
1049 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1050 while (NewFileName
[0] == '/')
1051 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1054 NewFileName
= FileName
;
1056 if (PathPrefix
.empty() == false)
1057 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1059 FileFd
fd(FileName
, FileFd::ReadOnly
);
1066 CheckSums
[NewFileName
].size
= fd
.Size();
1069 hs
.AddFD(fd
, 0, DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
);
1071 CheckSums
[NewFileName
].MD5
= hs
.MD5
.Result();
1073 CheckSums
[NewFileName
].SHA1
= hs
.SHA1
.Result();
1074 if (DoSHA256
== true)
1075 CheckSums
[NewFileName
].SHA256
= hs
.SHA256
.Result();
1076 if (DoSHA512
== true)
1077 CheckSums
[NewFileName
].SHA512
= hs
.SHA512
.Result();
1084 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1085 // ---------------------------------------------------------------------
1086 void ReleaseWriter::Finish()
1090 fprintf(Output
, "MD5Sum:\n");
1091 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1092 I
!= CheckSums
.end(); ++I
)
1094 fprintf(Output
, " %s %16llu %s\n",
1095 (*I
).second
.MD5
.c_str(),
1097 (*I
).first
.c_str());
1102 fprintf(Output
, "SHA1:\n");
1103 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1104 I
!= CheckSums
.end(); ++I
)
1106 fprintf(Output
, " %s %16llu %s\n",
1107 (*I
).second
.SHA1
.c_str(),
1109 (*I
).first
.c_str());
1112 if (DoSHA256
== true)
1114 fprintf(Output
, "SHA256:\n");
1115 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1116 I
!= CheckSums
.end(); ++I
)
1118 fprintf(Output
, " %s %16llu %s\n",
1119 (*I
).second
.SHA256
.c_str(),
1121 (*I
).first
.c_str());
1125 fprintf(Output
, "SHA512:\n");
1126 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1127 I
!= CheckSums
.end();
1130 fprintf(Output
, " %s %16llu %s\n",
1131 (*I
).second
.SHA512
.c_str(),
1133 (*I
).first
.c_str());