]>
git.saurik.com Git - apt.git/blob - ftparchive/writer.cc
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 // ConfigToDoHashes - which hashes to generate /*{{{*/
55 static void SingleConfigToDoHashes(unsigned int &DoHashes
, std::string
const &Conf
, unsigned int const Flag
)
57 if (_config
->FindB(Conf
, true) == true)
62 static void ConfigToDoHashes(unsigned int &DoHashes
, std::string
const &Conf
)
64 SingleConfigToDoHashes(DoHashes
, Conf
+ "::MD5", Hashes::MD5SUM
);
65 SingleConfigToDoHashes(DoHashes
, Conf
+ "::SHA1", Hashes::SHA1SUM
);
66 SingleConfigToDoHashes(DoHashes
, Conf
+ "::SHA256", Hashes::SHA256SUM
);
67 SingleConfigToDoHashes(DoHashes
, Conf
+ "::SHA512", Hashes::SHA512SUM
);
71 // FTWScanner::FTWScanner - Constructor /*{{{*/
72 FTWScanner::FTWScanner(FileFd
* const GivenOutput
, string
const &Arch
): Arch(Arch
), DoHashes(~0)
74 if (GivenOutput
== NULL
)
78 Output
->OpenDescriptor(STDOUT_FILENO
, FileFd::WriteOnly
, false);
86 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
87 ConfigToDoHashes(DoHashes
, "APT::FTPArchive");
90 FTWScanner::~FTWScanner()
92 if (Output
!= NULL
&& OwnsOutput
)
95 // FTWScanner::Scanner - FTW Scanner /*{{{*/
96 // ---------------------------------------------------------------------
97 /* This is the FTW scanner, it processes each directory element in the
99 int FTWScanner::ScannerFTW(const char *File
,const struct stat
* /*sb*/,int Flag
)
104 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
109 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
114 return ScannerFile(File
, true);
117 // FTWScanner::ScannerFile - File Scanner /*{{{*/
118 // ---------------------------------------------------------------------
120 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
122 const char *LastComponent
= strrchr(File
, '/');
123 char *RealPath
= NULL
;
125 if (LastComponent
== NULL
)
126 LastComponent
= File
;
130 vector
<string
>::const_iterator I
;
131 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
133 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
136 if (I
== Owner
->Patterns
.end())
139 /* Process it. If the file is a link then resolve it into an absolute
140 name.. This works best if the directory components the scanner are
141 given are not links themselves. */
143 Owner
->OriginalPath
= File
;
145 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
146 (RealPath
= realpath(File
,NULL
)) != 0)
148 Owner
->DoPackage(RealPath
);
152 Owner
->DoPackage(File
);
154 if (_error
->empty() == false)
156 // Print any errors or warnings found
158 bool SeenPath
= false;
159 while (_error
->empty() == false)
163 bool const Type
= _error
->PopMessage(Err
);
165 cerr
<< _("E: ") << Err
<< endl
;
167 cerr
<< _("W: ") << Err
<< endl
;
169 if (Err
.find(File
) != string::npos
)
173 if (SeenPath
== false)
174 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
181 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
182 // ---------------------------------------------------------------------
184 bool FTWScanner::RecursiveScan(string
const &Dir
)
186 char *RealPath
= NULL
;
187 /* If noprefix is set then jam the scan root in, so we don't generate
188 link followed paths out of control */
189 if (InternalPrefix
.empty() == true)
191 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
192 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
193 InternalPrefix
= RealPath
;
197 // Do recursive directory searching
199 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
201 // Error treewalking?
204 if (_error
->PendingError() == false)
205 _error
->Errno("ftw",_("Tree walking failed"));
212 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
213 // ---------------------------------------------------------------------
214 /* This is an alternative to using FTW to locate files, it reads the list
215 of files from another file. */
216 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
218 char *RealPath
= NULL
;
219 /* If noprefix is set then jam the scan root in, so we don't generate
220 link followed paths out of control */
221 if (InternalPrefix
.empty() == true)
223 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
224 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
225 InternalPrefix
= RealPath
;
230 FILE *List
= fopen(File
.c_str(),"r");
232 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
234 /* We are a tad tricky here.. We prefix the buffer with the directory
235 name, that way if we need a full path with just use line.. Sneaky and
239 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
240 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
242 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
243 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
245 char *FileName
= _strstrip(FileStart
);
246 if (FileName
[0] == 0)
249 if (FileName
[0] != '/')
251 if (FileName
!= FileStart
)
252 memmove(FileStart
,FileName
,strlen(FileStart
));
259 if (stat(FileName
,&St
) != 0)
263 if (ScannerFile(FileName
, false) != 0)
271 // FTWScanner::Delink - Delink symlinks /*{{{*/
272 // ---------------------------------------------------------------------
274 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
275 unsigned long long &DeLinkBytes
,
276 unsigned long long const &FileSize
)
278 // See if this isn't an internaly prefix'd file name.
279 if (InternalPrefix
.empty() == false &&
280 InternalPrefix
.length() < FileName
.length() &&
281 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
282 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
284 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
286 // Tidy up the display
287 if (DeLinkBytes
== 0)
291 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
292 SizeToStr(FileSize
).c_str());
295 if (NoLinkAct
== false)
298 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
299 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
302 if (unlink(OriginalPath
) != 0)
303 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
306 if (link(FileName
.c_str(),OriginalPath
) != 0)
308 // Panic! Restore the symlink
309 if (symlink(OldLink
,OriginalPath
) != 0)
310 _error
->Errno("symlink", "failed to restore symlink");
311 return _error
->Errno("link",_("*** Failed to link %s to %s"),
319 DeLinkBytes
+= FileSize
;
320 if (DeLinkBytes
/1024 >= DeLinkLimit
)
321 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
324 FileName
= OriginalPath
;
331 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
332 // ---------------------------------------------------------------------
334 PackagesWriter::PackagesWriter(FileFd
* const GivenOutput
, TranslationWriter
* const transWriter
,
335 string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
336 string
const &Arch
) :
337 FTWScanner(GivenOutput
, Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(transWriter
)
339 SetExts(".deb .udeb");
342 // Process the command line options
343 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Packages");
344 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
345 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
346 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
347 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
349 if (Db
.Loaded() == false)
352 // Read the override file
353 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
358 if (ExtOverrides
.empty() == false)
359 Over
.ReadExtraOverride(ExtOverrides
);
361 _error
->DumpErrors();
364 // FTWScanner::SetExts - Set extensions to support /*{{{*/
365 // ---------------------------------------------------------------------
367 bool FTWScanner::SetExts(string
const &Vals
)
370 string::size_type Start
= 0;
371 while (Start
<= Vals
.length()-1)
373 string::size_type
const Space
= Vals
.find(' ',Start
);
374 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
375 if ( Arch
.empty() == false )
377 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
378 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
381 AddPattern(string("*") + Vals
.substr(Start
, Length
));
389 // PackagesWriter::DoPackage - Process a single package /*{{{*/
390 // ---------------------------------------------------------------------
391 /* This method takes a package and gets its control information and
392 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
393 rewritten and the path/size/hash appended. */
394 bool PackagesWriter::DoPackage(string FileName
)
396 // Pull all the data we need form the DB
397 if (Db
.GetFileInfo(FileName
,
398 true, /* DoControl */
400 true, /* GenContentsOnly */
401 false, /* DoSource */
402 DoHashes
, DoAlwaysStat
) == false)
407 unsigned long long FileSize
= Db
.GetFileSize();
408 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
411 // Lookup the overide information
412 pkgTagSection
&Tags
= Db
.Control
.Section
;
413 string Package
= Tags
.FindS("Package");
415 // if we generate a Packages file for a given arch, we use it to
416 // look for overrides. if we run in "simple" mode without the
417 // "Architecures" variable in the config we use the architecure value
422 Architecture
= Tags
.FindS("Architecture");
423 unique_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
425 if (Package
.empty() == true)
426 return _error
->Error(_("Archive had no package field"));
428 // If we need to do any rewriting of the header do it now..
429 if (OverItem
.get() == 0)
431 if (NoOverride
== false)
434 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
437 OverItem
= unique_ptr
<Override::Item
>(new Override::Item
);
438 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
439 OverItem
->Priority
= Tags
.FindS("Priority");
442 // Strip the DirStrip prefix from the FileName and add the PathPrefix
444 if (DirStrip
.empty() == false &&
445 FileName
.length() > DirStrip
.length() &&
446 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
447 DirStrip
.begin(),DirStrip
.end()) == 0)
448 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
450 NewFileName
= FileName
;
451 if (PathPrefix
.empty() == false)
452 NewFileName
= flCombine(PathPrefix
,NewFileName
);
454 /* Configuration says we don't want to include the long Description
455 in the package file - instead we want to ship a separated file */
457 if (LongDescription
== false) {
458 desc
= Tags
.FindS("Description").append("\n");
459 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
462 // This lists all the changes to the fields we are going to make.
463 std::vector
<pkgTagSection::Tag
> Changes
;
466 strprintf(Size
, "%llu", (unsigned long long) FileSize
);
467 Changes
.push_back(pkgTagSection::Tag::Rewrite("Size", Size
));
469 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
471 if (hs
->HashType() == "MD5Sum")
472 Changes
.push_back(pkgTagSection::Tag::Rewrite("MD5sum", hs
->HashValue()));
473 else if (hs
->HashType() == "Checksum-FileSize")
476 Changes
.push_back(pkgTagSection::Tag::Rewrite(hs
->HashType(), hs
->HashValue()));
478 Changes
.push_back(pkgTagSection::Tag::Rewrite("Filename", NewFileName
));
479 Changes
.push_back(pkgTagSection::Tag::Rewrite("Priority", OverItem
->Priority
));
480 Changes
.push_back(pkgTagSection::Tag::Remove("Status"));
481 Changes
.push_back(pkgTagSection::Tag::Remove("Optional"));
483 string DescriptionMd5
;
484 if (LongDescription
== false) {
485 MD5Summation descmd5
;
486 descmd5
.Add(desc
.c_str());
487 DescriptionMd5
= descmd5
.Result().Value();
488 Changes
.push_back(pkgTagSection::Tag::Rewrite("Description-md5", DescriptionMd5
));
489 if (TransWriter
!= NULL
)
490 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
493 // Rewrite the maintainer field if necessary
495 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
496 if (MaintFailed
== true)
498 if (NoOverride
== false)
501 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
502 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
506 if (NewMaint
.empty() == false)
507 Changes
.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint
));
509 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
510 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
511 but dpkg does this append bit. So we do the append bit, at least that way the
512 status file and package file will remain similar. There are other transforms
513 but optional is the only legacy one still in use for some lazy reason. */
514 string OptionalStr
= Tags
.FindS("Optional");
515 if (OptionalStr
.empty() == false)
517 if (Tags
.FindS("Suggests").empty() == false)
518 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
519 Changes
.push_back(pkgTagSection::Tag::Rewrite("Suggests", OptionalStr
));
522 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
523 I
!= OverItem
->FieldOverride
.end(); ++I
)
524 Changes
.push_back(pkgTagSection::Tag::Rewrite(I
->first
, I
->second
));
526 // Rewrite and store the fields.
527 if (Tags
.Write(*Output
, TFRewritePackageOrder
, Changes
) == false ||
528 Output
->Write("\n", 1) == false)
534 PackagesWriter::~PackagesWriter() /*{{{*/
539 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
540 // ---------------------------------------------------------------------
541 /* Create a Translation-Master file for this Packages file */
542 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
543 mode_t
const &Permissions
) : Comp(NULL
), Output(NULL
)
545 if (File
.empty() == true)
548 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
549 Output
= &Comp
->Input
;
552 // TranslationWriter::DoPackage - Process a single package /*{{{*/
553 // ---------------------------------------------------------------------
554 /* Create a Translation-Master file for this Packages file */
555 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
561 // Different archs can include different versions and therefore
562 // different descriptions - so we need to check for both name and md5.
563 string
const Record
= Pkg
+ ":" + MD5
;
565 if (Included
.find(Record
) != Included
.end())
569 strprintf(out
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
570 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
571 Output
->Write(out
.c_str(), out
.length());
573 Included
.insert(Record
);
577 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
578 // ---------------------------------------------------------------------
580 TranslationWriter::~TranslationWriter()
587 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
588 // ---------------------------------------------------------------------
590 SourcesWriter::SourcesWriter(FileFd
* const GivenOutput
, string
const &DB
, string
const &BOverrides
,string
const &SOverrides
,
591 string
const &ExtOverrides
) :
592 FTWScanner(GivenOutput
), Db(DB
), Stats(Db
.Stats
)
599 // Process the command line options
600 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Sources");
601 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
602 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
604 // Read the override file
605 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
610 // WTF?? The logic above: if we can't read binary overrides, don't even try
611 // reading source overrides. if we can read binary overrides, then say there
612 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
614 if (ExtOverrides
.empty() == false)
615 SOver
.ReadExtraOverride(ExtOverrides
);
617 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
618 SOver
.ReadOverride(SOverrides
,true);
621 // SourcesWriter::DoPackage - Process a single package /*{{{*/
622 static std::string
getDscHash(unsigned int const DoHashes
,
623 Hashes::SupportedHashes
const DoIt
, pkgTagSection
&Tags
, char const * const FieldName
,
624 HashString
const * const Hash
, unsigned long long Size
, std::string FileName
)
626 if ((DoHashes
& DoIt
) != DoIt
|| Tags
.Exists(FieldName
) == false || Hash
== NULL
)
628 std::ostringstream out
;
629 out
<< "\n " << Hash
->HashValue() << " " << Size
<< " " << FileName
630 << "\n " << Tags
.FindS(FieldName
);
633 bool SourcesWriter::DoPackage(string FileName
)
635 // Pull all the data we need form the DB
636 if (Db
.GetFileInfo(FileName
,
637 false, /* DoControl */
638 false, /* DoContents */
639 false, /* GenContentsOnly */
641 DoHashes
, DoAlwaysStat
) == false)
646 // we need to perform a "write" here (this is what finish is doing)
647 // because the call to Db.GetFileInfo() in the loop will change
652 if (Tags
.Scan(Db
.Dsc
.Data
.c_str(), Db
.Dsc
.Data
.length()) == false)
653 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
655 if (Tags
.Exists("Source") == false)
656 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
659 // Lookup the overide information, finding first the best priority.
661 string Bins
= Tags
.FindS("Binary");
662 char Buffer
[Bins
.length() + 1];
663 unique_ptr
<Override::Item
> OverItem(nullptr);
664 if (Bins
.empty() == false)
666 strcpy(Buffer
,Bins
.c_str());
668 // Ignore too-long errors.
670 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
672 // Look at all the binaries
673 unsigned char BestPrioV
= pkgCache::State::Extra
;
674 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
676 unique_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
680 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
681 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
683 BestPrioV
= NewPrioV
;
684 BestPrio
= Itm
->Priority
;
687 if (OverItem
.get() == 0)
688 OverItem
= std::move(Itm
);
692 // If we need to do any rewriting of the header do it now..
693 if (OverItem
.get() == 0)
695 if (NoOverride
== false)
698 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
701 OverItem
.reset(new Override::Item
);
705 if (stat(FileName
.c_str(), &St
) != 0)
706 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
708 unique_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
709 // const unique_ptr<Override::Item> autoSOverItem(SOverItem);
710 if (SOverItem
.get() == 0)
712 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
713 SOverItem
= unique_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
714 if (SOverItem
.get() == 0)
716 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
717 SOverItem
= unique_ptr
<Override::Item
>(new Override::Item
);
718 *SOverItem
= *OverItem
;
722 // Add the dsc to the files hash list
723 string
const strippedName
= flNotDir(FileName
);
724 std::string
const Files
= getDscHash(DoHashes
, Hashes::MD5SUM
, Tags
, "Files", Db
.HashesList
.find("MD5Sum"), St
.st_size
, strippedName
);
725 std::string ChecksumsSha1
= getDscHash(DoHashes
, Hashes::SHA1SUM
, Tags
, "Checksums-Sha1", Db
.HashesList
.find("SHA1"), St
.st_size
, strippedName
);
726 std::string ChecksumsSha256
= getDscHash(DoHashes
, Hashes::SHA256SUM
, Tags
, "Checksums-Sha256", Db
.HashesList
.find("SHA256"), St
.st_size
, strippedName
);
727 std::string ChecksumsSha512
= getDscHash(DoHashes
, Hashes::SHA512SUM
, Tags
, "Checksums-Sha512", Db
.HashesList
.find("SHA512"), St
.st_size
, strippedName
);
729 // Strip the DirStrip prefix from the FileName and add the PathPrefix
731 if (DirStrip
.empty() == false &&
732 FileName
.length() > DirStrip
.length() &&
733 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
734 NewFileName
= string(OriginalPath
+ DirStrip
.length());
736 NewFileName
= OriginalPath
;
737 if (PathPrefix
.empty() == false)
738 NewFileName
= flCombine(PathPrefix
,NewFileName
);
740 string Directory
= flNotFile(OriginalPath
);
741 string Package
= Tags
.FindS("Source");
743 // Perform operation over all of the files
745 const char *C
= Files
.c_str();
746 char *RealPath
= NULL
;
747 for (;isspace(*C
); C
++);
750 // Parse each of the elements
751 if (ParseQuoteWord(C
,ParseJnk
) == false ||
752 ParseQuoteWord(C
,ParseJnk
) == false ||
753 ParseQuoteWord(C
,ParseJnk
) == false)
754 return _error
->Error("Error parsing file record");
756 string OriginalPath
= Directory
+ ParseJnk
;
758 // Add missing hashes to source files
759 if (((DoHashes
& Hashes::SHA1SUM
) == Hashes::SHA1SUM
&& !Tags
.Exists("Checksums-Sha1")) ||
760 ((DoHashes
& Hashes::SHA256SUM
) == Hashes::SHA256SUM
&& !Tags
.Exists("Checksums-Sha256")) ||
761 ((DoHashes
& Hashes::SHA512SUM
) == Hashes::SHA512SUM
&& !Tags
.Exists("Checksums-Sha512")))
763 if (Db
.GetFileInfo(OriginalPath
,
764 false, /* DoControl */
765 false, /* DoContents */
766 false, /* GenContentsOnly */
767 false, /* DoSource */
769 DoAlwaysStat
) == false)
771 return _error
->Error("Error getting file info");
774 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
776 if (hs
->HashType() == "MD5Sum" || hs
->HashType() == "Checksum-FileSize")
778 char const * fieldname
;
780 if (hs
->HashType() == "SHA1")
782 fieldname
= "Checksums-Sha1";
783 out
= &ChecksumsSha1
;
785 else if (hs
->HashType() == "SHA256")
787 fieldname
= "Checksums-Sha256";
788 out
= &ChecksumsSha256
;
790 else if (hs
->HashType() == "SHA512")
792 fieldname
= "Checksums-Sha512";
793 out
= &ChecksumsSha512
;
797 _error
->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs
->HashType().c_str());
800 if (Tags
.Exists(fieldname
) == true)
802 std::ostringstream streamout
;
803 streamout
<< "\n " << hs
->HashValue() << " " << Db
.GetFileSize() << " " << ParseJnk
;
804 out
->append(streamout
.str());
807 // write back the GetFileInfo() stats data
811 // Perform the delinking operation
814 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
815 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
817 string RP
= RealPath
;
819 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
824 Directory
= flNotFile(NewFileName
);
825 if (Directory
.length() > 2)
826 Directory
.erase(Directory
.end()-1);
828 // This lists all the changes to the fields we are going to make.
829 // (5 hardcoded + checksums + maintainer + end marker)
830 std::vector
<pkgTagSection::Tag
> Changes
;
832 Changes
.push_back(pkgTagSection::Tag::Remove("Source"));
833 Changes
.push_back(pkgTagSection::Tag::Rewrite("Package", Package
));
834 if (Files
.empty() == false)
835 Changes
.push_back(pkgTagSection::Tag::Rewrite("Files", Files
));
836 if (ChecksumsSha1
.empty() == false)
837 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha1", ChecksumsSha1
));
838 if (ChecksumsSha256
.empty() == false)
839 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha256", ChecksumsSha256
));
840 if (ChecksumsSha512
.empty() == false)
841 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha512", ChecksumsSha512
));
842 if (Directory
!= "./")
843 Changes
.push_back(pkgTagSection::Tag::Rewrite("Directory", Directory
));
844 Changes
.push_back(pkgTagSection::Tag::Rewrite("Priority", BestPrio
));
845 Changes
.push_back(pkgTagSection::Tag::Remove("Status"));
847 // Rewrite the maintainer field if necessary
849 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"), MaintFailed
);
850 if (MaintFailed
== true)
852 if (NoOverride
== false)
855 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
856 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
859 if (NewMaint
.empty() == false)
860 Changes
.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint
.c_str()));
862 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
863 I
!= SOverItem
->FieldOverride
.end(); ++I
)
864 Changes
.push_back(pkgTagSection::Tag::Rewrite(I
->first
, I
->second
));
866 // Rewrite and store the fields.
867 if (Tags
.Write(*Output
, TFRewriteSourceOrder
, Changes
) == false ||
868 Output
->Write("\n", 1) == false)
877 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
878 // ---------------------------------------------------------------------
880 ContentsWriter::ContentsWriter(FileFd
* const GivenOutput
, string
const &DB
, string
const &Arch
) :
881 FTWScanner(GivenOutput
, Arch
), Db(DB
), Stats(Db
.Stats
)
887 // ContentsWriter::DoPackage - Process a single package /*{{{*/
888 // ---------------------------------------------------------------------
889 /* If Package is the empty string the control record will be parsed to
890 determine what the package name is. */
891 bool ContentsWriter::DoPackage(string FileName
, string Package
)
893 if (!Db
.GetFileInfo(FileName
,
894 Package
.empty(), /* DoControl */
895 true, /* DoContents */
896 false, /* GenContentsOnly */
897 false, /* DoSource */
899 false /* checkMtime */))
904 // Parse the package name
905 if (Package
.empty() == true)
907 Package
= Db
.Control
.Section
.FindS("Package");
910 Db
.Contents
.Add(Gen
,Package
);
915 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
916 // ---------------------------------------------------------------------
918 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
920 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
921 if (_error
->PendingError() == true)
924 // Open the package file
926 if (Pkgs
.OpenOld(Fd
) == false)
929 pkgTagFile
Tags(&Fd
);
930 if (_error
->PendingError() == true)
934 pkgTagSection Section
;
935 while (Tags
.Step(Section
) == true)
937 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
938 string Package
= Section
.FindS("Section");
939 if (Package
.empty() == false && Package
.end()[-1] != '/')
942 Package
+= Section
.FindS("Package");
945 Package
+= Section
.FindS("Package");
947 DoPackage(File
,Package
);
948 if (_error
->empty() == false)
950 _error
->Error("Errors apply to file '%s'",File
.c_str());
951 _error
->DumpErrors();
955 // Tidy the compressor
963 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
964 // ---------------------------------------------------------------------
966 ReleaseWriter::ReleaseWriter(FileFd
* const GivenOutput
, string
const &/*DB*/) : FTWScanner(GivenOutput
)
968 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
970 AddPattern("Packages");
971 AddPattern("Packages.gz");
972 AddPattern("Packages.bz2");
973 AddPattern("Packages.lzma");
974 AddPattern("Packages.xz");
975 AddPattern("Translation-*");
976 AddPattern("Sources");
977 AddPattern("Sources.gz");
978 AddPattern("Sources.bz2");
979 AddPattern("Sources.lzma");
980 AddPattern("Sources.xz");
981 AddPattern("Release");
982 AddPattern("Contents-*");
984 AddPattern("md5sum.txt");
986 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
988 time_t const now
= time(NULL
);
990 setlocale(LC_TIME
, "C");
993 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
999 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
1001 if (now
== validuntil
||
1002 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
1003 gmtime(&validuntil
)) == 0)
1008 setlocale(LC_TIME
, "");
1010 map
<string
,string
> Fields
;
1011 Fields
["Origin"] = "";
1012 Fields
["Label"] = "";
1013 Fields
["Suite"] = "";
1014 Fields
["Version"] = "";
1015 Fields
["Codename"] = "";
1016 Fields
["Date"] = datestr
;
1017 Fields
["Valid-Until"] = validstr
;
1018 Fields
["Architectures"] = "";
1019 Fields
["Components"] = "";
1020 Fields
["Description"] = "";
1022 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1026 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1027 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1031 std::string
const out
= I
->first
+ ": " + Value
+ "\n";
1032 Output
->Write(out
.c_str(), out
.length());
1035 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Release");
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();
1068 Hashes
hs(DoHashes
);
1070 CheckSums
[NewFileName
].Hashes
= hs
.GetHashStringList();
1077 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1078 // ---------------------------------------------------------------------
1079 static void printChecksumTypeRecord(FileFd
&Output
, char const * const Type
, map
<string
, ReleaseWriter::CheckSum
> const &CheckSums
)
1083 strprintf(out
, "%s:\n", Type
);
1084 Output
.Write(out
.c_str(), out
.length());
1086 for(map
<string
,ReleaseWriter::CheckSum
>::const_iterator I
= CheckSums
.begin();
1087 I
!= CheckSums
.end(); ++I
)
1089 HashString
const * const hs
= I
->second
.Hashes
.find(Type
);
1093 strprintf(out
, " %s %16llu %s\n",
1094 hs
->HashValue().c_str(),
1096 (*I
).first
.c_str());
1097 Output
.Write(out
.c_str(), out
.length());
1100 void ReleaseWriter::Finish()
1102 if ((DoHashes
& Hashes::MD5SUM
) == Hashes::MD5SUM
)
1103 printChecksumTypeRecord(*Output
, "MD5Sum", CheckSums
);
1104 if ((DoHashes
& Hashes::SHA1SUM
) == Hashes::SHA1SUM
)
1105 printChecksumTypeRecord(*Output
, "SHA1", CheckSums
);
1106 if ((DoHashes
& Hashes::SHA256SUM
) == Hashes::SHA256SUM
)
1107 printChecksumTypeRecord(*Output
, "SHA256", CheckSums
);
1108 if ((DoHashes
& Hashes::SHA512SUM
) == Hashes::SHA512SUM
)
1109 printChecksumTypeRecord(*Output
, "SHA512", CheckSums
);