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 /*{{{*/
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/aptconfiguration.h>
21 #include <apt-pkg/md5.h>
22 #include <apt-pkg/hashes.h>
23 #include <apt-pkg/deblistparser.h>
25 #include <sys/types.h>
35 #include "apt-ftparchive.h"
36 #include "multicompress.h"
39 FTWScanner
*FTWScanner::Owner
;
41 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
42 // ---------------------------------------------------------------------
44 inline void SetTFRewriteData(struct TFRewriteData
&tfrd
,
47 const char *newtag
= 0)
50 tfrd
.Rewrite
= rewrite
;
55 // FTWScanner::FTWScanner - Constructor /*{{{*/
56 // ---------------------------------------------------------------------
58 FTWScanner::FTWScanner(string
const &Arch
): Arch(Arch
)
61 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
63 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
64 DoSHA1
= _config
->FindB("APT::FTPArchive::SHA1",true);
65 DoSHA256
= _config
->FindB("APT::FTPArchive::SHA256",true);
66 DoSHA512
= _config
->FindB("APT::FTPArchive::SHA512",true);
69 // FTWScanner::Scanner - FTW Scanner /*{{{*/
70 // ---------------------------------------------------------------------
71 /* This is the FTW scanner, it processes each directory element in the
73 int FTWScanner::ScannerFTW(const char *File
,const struct stat
*sb
,int Flag
)
78 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
83 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
88 return ScannerFile(File
, true);
91 // FTWScanner::ScannerFile - File Scanner /*{{{*/
92 // ---------------------------------------------------------------------
94 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
96 const char *LastComponent
= strrchr(File
, '/');
97 char *RealPath
= NULL
;
99 if (LastComponent
== NULL
)
100 LastComponent
= File
;
104 vector
<string
>::const_iterator I
;
105 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
107 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
110 if (I
== Owner
->Patterns
.end())
113 /* Process it. If the file is a link then resolve it into an absolute
114 name.. This works best if the directory components the scanner are
115 given are not links themselves. */
117 Owner
->OriginalPath
= File
;
119 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
120 (RealPath
= realpath(File
,NULL
)) != 0)
122 Owner
->DoPackage(RealPath
);
126 Owner
->DoPackage(File
);
128 if (_error
->empty() == false)
130 // Print any errors or warnings found
132 bool SeenPath
= false;
133 while (_error
->empty() == false)
137 bool const Type
= _error
->PopMessage(Err
);
139 cerr
<< _("E: ") << Err
<< endl
;
141 cerr
<< _("W: ") << Err
<< endl
;
143 if (Err
.find(File
) != string::npos
)
147 if (SeenPath
== false)
148 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
155 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
156 // ---------------------------------------------------------------------
158 bool FTWScanner::RecursiveScan(string
const &Dir
)
160 char *RealPath
= NULL
;
161 /* If noprefix is set then jam the scan root in, so we don't generate
162 link followed paths out of control */
163 if (InternalPrefix
.empty() == true)
165 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
166 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
167 InternalPrefix
= RealPath
;
171 // Do recursive directory searching
173 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
175 // Error treewalking?
178 if (_error
->PendingError() == false)
179 _error
->Errno("ftw",_("Tree walking failed"));
186 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
187 // ---------------------------------------------------------------------
188 /* This is an alternative to using FTW to locate files, it reads the list
189 of files from another file. */
190 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
192 char *RealPath
= NULL
;
193 /* If noprefix is set then jam the scan root in, so we don't generate
194 link followed paths out of control */
195 if (InternalPrefix
.empty() == true)
197 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
198 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
199 InternalPrefix
= RealPath
;
204 FILE *List
= fopen(File
.c_str(),"r");
206 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
208 /* We are a tad tricky here.. We prefix the buffer with the directory
209 name, that way if we need a full path with just use line.. Sneaky and
213 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
214 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
216 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
217 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
219 char *FileName
= _strstrip(FileStart
);
220 if (FileName
[0] == 0)
223 if (FileName
[0] != '/')
225 if (FileName
!= FileStart
)
226 memmove(FileStart
,FileName
,strlen(FileStart
));
233 if (stat(FileName
,&St
) != 0)
237 if (ScannerFile(FileName
, false) != 0)
245 // FTWScanner::Delink - Delink symlinks /*{{{*/
246 // ---------------------------------------------------------------------
248 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
249 unsigned long &DeLinkBytes
,
250 off_t
const &FileSize
)
252 // See if this isn't an internaly prefix'd file name.
253 if (InternalPrefix
.empty() == false &&
254 InternalPrefix
.length() < FileName
.length() &&
255 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
256 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
258 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
260 // Tidy up the display
261 if (DeLinkBytes
== 0)
265 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
266 SizeToStr(FileSize
).c_str());
269 if (NoLinkAct
== false)
272 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
273 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
276 if (unlink(OriginalPath
) != 0)
277 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
280 if (link(FileName
.c_str(),OriginalPath
) != 0)
282 // Panic! Restore the symlink
283 symlink(OldLink
,OriginalPath
);
284 return _error
->Errno("link",_("*** Failed to link %s to %s"),
292 DeLinkBytes
+= FileSize
;
293 if (DeLinkBytes
/1024 >= DeLinkLimit
)
294 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
297 FileName
= OriginalPath
;
304 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
305 // ---------------------------------------------------------------------
307 PackagesWriter::PackagesWriter(string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
308 string
const &Arch
) :
309 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
312 SetExts(".deb .udeb");
315 // Process the command line options
316 DoMD5
= _config
->FindB("APT::FTPArchive::Packages::MD5",DoMD5
);
317 DoSHA1
= _config
->FindB("APT::FTPArchive::Packages::SHA1",DoSHA1
);
318 DoSHA256
= _config
->FindB("APT::FTPArchive::Packages::SHA256",DoSHA256
);
319 DoSHA256
= _config
->FindB("APT::FTPArchive::Packages::SHA512",true);
320 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
321 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
322 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
323 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
325 if (Db
.Loaded() == false)
328 // Read the override file
329 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
334 if (ExtOverrides
.empty() == false)
335 Over
.ReadExtraOverride(ExtOverrides
);
337 _error
->DumpErrors();
340 // FTWScanner::SetExts - Set extensions to support /*{{{*/
341 // ---------------------------------------------------------------------
343 bool FTWScanner::SetExts(string
const &Vals
)
346 string::size_type Start
= 0;
347 while (Start
<= Vals
.length()-1)
349 string::size_type
const Space
= Vals
.find(' ',Start
);
350 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
351 if ( Arch
.empty() == false )
353 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
354 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
357 AddPattern(string("*") + Vals
.substr(Start
, Length
));
366 // PackagesWriter::DoPackage - Process a single package /*{{{*/
367 // ---------------------------------------------------------------------
368 /* This method takes a package and gets its control information and
369 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
370 rewritten and the path/size/hash appended. */
371 bool PackagesWriter::DoPackage(string FileName
)
373 // Pull all the data we need form the DB
374 if (Db
.GetFileInfo(FileName
, true, DoContents
, true, DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
, DoAlwaysStat
)
380 off_t FileSize
= Db
.GetFileSize();
381 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
384 // Lookup the overide information
385 pkgTagSection
&Tags
= Db
.Control
.Section
;
386 string Package
= Tags
.FindS("Package");
388 // if we generate a Packages file for a given arch, we use it to
389 // look for overrides. if we run in "simple" mode without the
390 // "Architecures" variable in the config we use the architecure value
395 Architecture
= Tags
.FindS("Architecture");
396 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
398 if (Package
.empty() == true)
399 return _error
->Error(_("Archive had no package field"));
401 // If we need to do any rewriting of the header do it now..
402 if (OverItem
.get() == 0)
404 if (NoOverride
== false)
407 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
410 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
411 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
412 OverItem
->Priority
= Tags
.FindS("Priority");
416 sprintf(Size
,"%lu", (unsigned long) FileSize
);
418 // Strip the DirStrip prefix from the FileName and add the PathPrefix
420 if (DirStrip
.empty() == false &&
421 FileName
.length() > DirStrip
.length() &&
422 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
423 DirStrip
.begin(),DirStrip
.end()) == 0)
424 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
426 NewFileName
= FileName
;
427 if (PathPrefix
.empty() == false)
428 NewFileName
= flCombine(PathPrefix
,NewFileName
);
430 /* Configuration says we don't want to include the long Description
431 in the package file - instead we want to ship a separated file */
433 if (LongDescription
== false) {
434 desc
= Tags
.FindS("Description").append("\n");
435 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
438 // This lists all the changes to the fields we are going to make.
439 // (7 hardcoded + maintainer + suggests + end marker)
440 TFRewriteData Changes
[6+2+OverItem
->FieldOverride
.size()+1+1];
442 unsigned int End
= 0;
443 SetTFRewriteData(Changes
[End
++], "Size", Size
);
445 SetTFRewriteData(Changes
[End
++], "MD5sum", Db
.MD5Res
.c_str());
447 SetTFRewriteData(Changes
[End
++], "SHA1", Db
.SHA1Res
.c_str());
448 if (DoSHA256
== true)
449 SetTFRewriteData(Changes
[End
++], "SHA256", Db
.SHA256Res
.c_str());
450 if (DoSHA512
== true)
451 SetTFRewriteData(Changes
[End
++], "SHA512", Db
.SHA512Res
.c_str());
452 SetTFRewriteData(Changes
[End
++], "Filename", NewFileName
.c_str());
453 SetTFRewriteData(Changes
[End
++], "Priority", OverItem
->Priority
.c_str());
454 SetTFRewriteData(Changes
[End
++], "Status", 0);
455 SetTFRewriteData(Changes
[End
++], "Optional", 0);
457 string DescriptionMd5
;
458 if (LongDescription
== false) {
459 MD5Summation descmd5
;
460 descmd5
.Add(desc
.c_str());
461 DescriptionMd5
= descmd5
.Result().Value();
462 SetTFRewriteData(Changes
[End
++], "Description-md5", DescriptionMd5
.c_str());
463 if (TransWriter
!= NULL
)
464 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
467 // Rewrite the maintainer field if necessary
469 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
470 if (MaintFailed
== true)
472 if (NoOverride
== false)
475 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
476 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
480 if (NewMaint
.empty() == false)
481 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
483 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
484 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
485 but dpkg does this append bit. So we do the append bit, at least that way the
486 status file and package file will remain similar. There are other transforms
487 but optional is the only legacy one still in use for some lazy reason. */
488 string OptionalStr
= Tags
.FindS("Optional");
489 if (OptionalStr
.empty() == false)
491 if (Tags
.FindS("Suggests").empty() == false)
492 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
493 SetTFRewriteData(Changes
[End
++], "Suggests", OptionalStr
.c_str());
496 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
497 I
!= OverItem
->FieldOverride
.end(); I
++)
498 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
500 SetTFRewriteData(Changes
[End
++], 0, 0);
502 // Rewrite and store the fields.
503 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
) == false)
505 fprintf(Output
,"\n");
511 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
512 // ---------------------------------------------------------------------
513 /* Create a Translation-Master file for this Packages file */
514 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
515 mode_t
const &Permissions
) : Output(NULL
),
518 if (File
.empty() == true)
521 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
522 Output
= Comp
->Input
;
525 // TranslationWriter::DoPackage - Process a single package /*{{{*/
526 // ---------------------------------------------------------------------
527 /* Create a Translation-Master file for this Packages file */
528 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
534 // Different archs can include different versions and therefore
535 // different descriptions - so we need to check for both name and md5.
536 string
const Record
= Pkg
+ ":" + MD5
;
538 if (Included
.find(Record
) != Included
.end())
541 fprintf(Output
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
542 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
544 Included
.insert(Record
);
548 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
549 // ---------------------------------------------------------------------
551 TranslationWriter::~TranslationWriter()
560 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
561 // ---------------------------------------------------------------------
563 SourcesWriter::SourcesWriter(string
const &BOverrides
,string
const &SOverrides
,
564 string
const &ExtOverrides
)
572 // Process the command line options
573 DoMD5
= _config
->FindB("APT::FTPArchive::Sources::MD5",DoMD5
);
574 DoSHA1
= _config
->FindB("APT::FTPArchive::Sources::SHA1",DoSHA1
);
575 DoSHA256
= _config
->FindB("APT::FTPArchive::Sources::SHA256",DoSHA256
);
576 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
578 // Read the override file
579 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
584 // WTF?? The logic above: if we can't read binary overrides, don't even try
585 // reading source overrides. if we can read binary overrides, then say there
586 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
588 if (ExtOverrides
.empty() == false)
589 SOver
.ReadExtraOverride(ExtOverrides
);
591 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
592 SOver
.ReadOverride(SOverrides
,true);
595 // SourcesWriter::DoPackage - Process a single package /*{{{*/
596 // ---------------------------------------------------------------------
598 bool SourcesWriter::DoPackage(string FileName
)
601 FileFd
F(FileName
,FileFd::ReadOnly
);
602 if (_error
->PendingError() == true)
605 // Stat the file for later
607 if (fstat(F
.Fd(),&St
) != 0)
608 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
610 if (St
.st_size
> 128*1024)
611 return _error
->Error("DSC file '%s' is too large!",FileName
.c_str());
613 if (BufSize
< (unsigned)St
.st_size
+1)
615 BufSize
= St
.st_size
+1;
616 Buffer
= (char *)realloc(Buffer
,St
.st_size
+1);
619 if (F
.Read(Buffer
,St
.st_size
) == false)
623 char *Start
= Buffer
;
624 char *BlkEnd
= Buffer
+ St
.st_size
;
628 SHA256Summation SHA256
;
629 SHA256Summation SHA512
;
632 MD5
.Add((unsigned char *)Start
,BlkEnd
- Start
);
634 SHA1
.Add((unsigned char *)Start
,BlkEnd
- Start
);
635 if (DoSHA256
== true)
636 SHA256
.Add((unsigned char *)Start
,BlkEnd
- Start
);
637 if (DoSHA512
== true)
638 SHA512
.Add((unsigned char *)Start
,BlkEnd
- Start
);
640 // Add an extra \n to the end, just in case
643 /* Remove the PGP trailer. Some .dsc's have this without a blank line
645 const char *Key
= "-----BEGIN PGP SIGNATURE-----";
646 for (char *MsgEnd
= Start
; MsgEnd
< BlkEnd
- strlen(Key
) -1; MsgEnd
++)
648 if (*MsgEnd
== '\n' && strncmp(MsgEnd
+1,Key
,strlen(Key
)) == 0)
655 /* Read records until we locate the Source record. This neatly skips the
656 GPG header (which is RFC822 formed) without any trouble. */
661 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
662 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
663 if (Tags
.Find("Source",Pos
) == true)
665 Start
+= Tags
.size();
670 // Lookup the overide information, finding first the best priority.
672 string Bins
= Tags
.FindS("Binary");
673 char Buffer
[Bins
.length() + 1];
674 auto_ptr
<Override::Item
> OverItem(0);
675 if (Bins
.empty() == false)
677 strcpy(Buffer
,Bins
.c_str());
679 // Ignore too-long errors.
681 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
683 // Look at all the binaries
684 unsigned char BestPrioV
= pkgCache::State::Extra
;
685 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
687 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
691 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
692 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
694 BestPrioV
= NewPrioV
;
695 BestPrio
= Itm
->Priority
;
698 if (OverItem
.get() == 0)
703 // If we need to do any rewriting of the header do it now..
704 if (OverItem
.get() == 0)
706 if (NoOverride
== false)
709 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
712 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
715 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
716 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
717 if (SOverItem
.get() == 0)
719 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
720 SOverItem
= auto_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
721 if (SOverItem
.get() == 0)
723 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
724 SOverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
725 *SOverItem
= *OverItem
;
729 // Add the dsc to the files hash list
730 string
const strippedName
= flNotDir(FileName
);
731 std::ostringstream ostreamFiles
;
732 if (DoMD5
== true && Tags
.Exists("Files"))
733 ostreamFiles
<< "\n " << string(MD5
.Result()) << " " << St
.st_size
<< " "
734 << strippedName
<< "\n " << Tags
.FindS("Files");
735 string
const Files
= ostreamFiles
.str();
737 std::ostringstream ostreamSha1
;
738 if (DoSHA1
== true && Tags
.Exists("Checksums-Sha1"))
739 ostreamSha1
<< "\n " << string(SHA1
.Result()) << " " << St
.st_size
<< " "
740 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha1");
741 string
const ChecksumsSha1
= ostreamSha1
.str();
743 std::ostringstream ostreamSha256
;
744 if (DoSHA256
== true && Tags
.Exists("Checksums-Sha256"))
745 ostreamSha256
<< "\n " << string(SHA256
.Result()) << " " << St
.st_size
<< " "
746 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha256");
747 string
const ChecksumsSha256
= ostreamSha256
.str();
749 std::ostringstream ostreamSha512
;
750 if (Tags
.Exists("Checksums-Sha512"))
751 ostreamSha512
<< "\n " << string(SHA512
.Result()) << " " << St
.st_size
<< " "
752 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha512");
753 string
const ChecksumsSha512
= ostreamSha512
.str();
755 // Strip the DirStrip prefix from the FileName and add the PathPrefix
757 if (DirStrip
.empty() == false &&
758 FileName
.length() > DirStrip
.length() &&
759 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
760 NewFileName
= string(OriginalPath
+ DirStrip
.length());
762 NewFileName
= OriginalPath
;
763 if (PathPrefix
.empty() == false)
764 NewFileName
= flCombine(PathPrefix
,NewFileName
);
766 string Directory
= flNotFile(OriginalPath
);
767 string Package
= Tags
.FindS("Source");
769 // Perform the delinking operation over all of the files
771 const char *C
= Files
.c_str();
772 char *RealPath
= NULL
;
773 for (;isspace(*C
); C
++);
776 // Parse each of the elements
777 if (ParseQuoteWord(C
,ParseJnk
) == false ||
778 ParseQuoteWord(C
,ParseJnk
) == false ||
779 ParseQuoteWord(C
,ParseJnk
) == false)
780 return _error
->Error("Error parsing file record");
783 string OriginalPath
= Directory
+ ParseJnk
;
784 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
785 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
787 string RP
= RealPath
;
789 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
794 Directory
= flNotFile(NewFileName
);
795 if (Directory
.length() > 2)
796 Directory
.erase(Directory
.end()-1);
798 // This lists all the changes to the fields we are going to make.
799 // (5 hardcoded + checksums + maintainer + end marker)
800 TFRewriteData Changes
[5+2+1+SOverItem
->FieldOverride
.size()+1];
802 unsigned int End
= 0;
803 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
804 if (Files
.empty() == false)
805 SetTFRewriteData(Changes
[End
++],"Files",Files
.c_str());
806 if (ChecksumsSha1
.empty() == false)
807 SetTFRewriteData(Changes
[End
++],"Checksums-Sha1",ChecksumsSha1
.c_str());
808 if (ChecksumsSha256
.empty() == false)
809 SetTFRewriteData(Changes
[End
++],"Checksums-Sha256",ChecksumsSha256
.c_str());
810 if (ChecksumsSha512
.empty() == false)
811 SetTFRewriteData(Changes
[End
++],"Checksums-Sha512",ChecksumsSha512
.c_str());
812 if (Directory
!= "./")
813 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
814 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
815 SetTFRewriteData(Changes
[End
++],"Status",0);
817 // Rewrite the maintainer field if necessary
819 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
820 if (MaintFailed
== true)
822 if (NoOverride
== false)
825 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
826 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
829 if (NewMaint
.empty() == false)
830 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
832 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
833 I
!= SOverItem
->FieldOverride
.end(); I
++)
834 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
836 SetTFRewriteData(Changes
[End
++], 0, 0);
838 // Rewrite and store the fields.
839 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
841 fprintf(Output
,"\n");
849 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
850 // ---------------------------------------------------------------------
852 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
853 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
860 // ContentsWriter::DoPackage - Process a single package /*{{{*/
861 // ---------------------------------------------------------------------
862 /* If Package is the empty string the control record will be parsed to
863 determine what the package name is. */
864 bool ContentsWriter::DoPackage(string FileName
, string Package
)
866 if (!Db
.GetFileInfo(FileName
, Package
.empty(), true, false, false, false, false, false))
871 // Parse the package name
872 if (Package
.empty() == true)
874 Package
= Db
.Control
.Section
.FindS("Package");
877 Db
.Contents
.Add(Gen
,Package
);
882 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
883 // ---------------------------------------------------------------------
885 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
887 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
888 if (_error
->PendingError() == true)
891 // Open the package file
894 if (Pkgs
.OpenOld(CompFd
,Proc
) == false)
898 FileFd
Fd(CompFd
,false);
899 pkgTagFile
Tags(&Fd
);
900 if (_error
->PendingError() == true)
902 Pkgs
.CloseOld(CompFd
,Proc
);
907 pkgTagSection Section
;
908 while (Tags
.Step(Section
) == true)
910 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
911 string Package
= Section
.FindS("Section");
912 if (Package
.empty() == false && Package
.end()[-1] != '/')
915 Package
+= Section
.FindS("Package");
918 Package
+= Section
.FindS("Package");
920 DoPackage(File
,Package
);
921 if (_error
->empty() == false)
923 _error
->Error("Errors apply to file '%s'",File
.c_str());
924 _error
->DumpErrors();
928 // Tidy the compressor
929 if (Pkgs
.CloseOld(CompFd
,Proc
) == false)
937 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
938 // ---------------------------------------------------------------------
940 ReleaseWriter::ReleaseWriter(string
const &DB
)
942 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
944 AddPattern("Packages");
945 AddPattern("Packages.gz");
946 AddPattern("Packages.bz2");
947 AddPattern("Packages.lzma");
948 AddPattern("Packages.xz");
949 AddPattern("Sources");
950 AddPattern("Sources.gz");
951 AddPattern("Sources.bz2");
952 AddPattern("Sources.lzma");
953 AddPattern("Sources.xz");
954 AddPattern("Release");
956 AddPattern("md5sum.txt");
958 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
961 time_t const now
= time(NULL
);
963 setlocale(LC_TIME
, "C");
966 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
972 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
974 if (now
== validuntil
||
975 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
976 gmtime(&validuntil
)) == 0)
981 setlocale(LC_TIME
, "");
983 map
<string
,string
> Fields
;
984 Fields
["Origin"] = "";
985 Fields
["Label"] = "";
986 Fields
["Suite"] = "";
987 Fields
["Version"] = "";
988 Fields
["Codename"] = "";
989 Fields
["Date"] = datestr
;
990 Fields
["Valid-Until"] = validstr
;
991 Fields
["Architectures"] = "";
992 Fields
["Components"] = "";
993 Fields
["Description"] = "";
995 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
999 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1000 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1004 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
1007 DoMD5
= _config
->FindB("APT::FTPArchive::Release::MD5",DoMD5
);
1008 DoSHA1
= _config
->FindB("APT::FTPArchive::Release::SHA1",DoSHA1
);
1009 DoSHA256
= _config
->FindB("APT::FTPArchive::Release::SHA256",DoSHA256
);
1012 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1013 // ---------------------------------------------------------------------
1014 bool ReleaseWriter::DoPackage(string FileName
)
1016 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1018 if (DirStrip
.empty() == false &&
1019 FileName
.length() > DirStrip
.length() &&
1020 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1021 DirStrip
.begin(),DirStrip
.end()) == 0)
1023 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1024 while (NewFileName
[0] == '/')
1025 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1028 NewFileName
= FileName
;
1030 if (PathPrefix
.empty() == false)
1031 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1033 FileFd
fd(FileName
, FileFd::ReadOnly
);
1040 CheckSums
[NewFileName
].size
= fd
.Size();
1043 hs
.AddFD(fd
.Fd(), 0, DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
);
1045 CheckSums
[NewFileName
].MD5
= hs
.MD5
.Result();
1047 CheckSums
[NewFileName
].SHA1
= hs
.SHA1
.Result();
1048 if (DoSHA256
== true)
1049 CheckSums
[NewFileName
].SHA256
= hs
.SHA256
.Result();
1050 if (DoSHA512
== true)
1051 CheckSums
[NewFileName
].SHA512
= hs
.SHA512
.Result();
1058 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1059 // ---------------------------------------------------------------------
1060 void ReleaseWriter::Finish()
1064 fprintf(Output
, "MD5Sum:\n");
1065 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1066 I
!= CheckSums
.end(); ++I
)
1068 fprintf(Output
, " %s %16ld %s\n",
1069 (*I
).second
.MD5
.c_str(),
1071 (*I
).first
.c_str());
1076 fprintf(Output
, "SHA1:\n");
1077 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1078 I
!= CheckSums
.end(); ++I
)
1080 fprintf(Output
, " %s %16ld %s\n",
1081 (*I
).second
.SHA1
.c_str(),
1083 (*I
).first
.c_str());
1086 if (DoSHA256
== true)
1088 fprintf(Output
, "SHA256:\n");
1089 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1090 I
!= CheckSums
.end(); ++I
)
1092 fprintf(Output
, " %s %16ld %s\n",
1093 (*I
).second
.SHA256
.c_str(),
1095 (*I
).first
.c_str());
1099 fprintf(Output
, "SHA512:\n");
1100 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1101 I
!= CheckSums
.end();
1104 fprintf(Output
, " %s %32ld %s\n",
1105 (*I
).second
.SHA512
.c_str(),
1107 (*I
).first
.c_str());