]>
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 // ---------------------------------------------------------------------
74 FTWScanner::FTWScanner(FileFd
* const GivenOutput
, string
const &Arch
): Arch(Arch
), DoHashes(~0)
76 if (GivenOutput
== NULL
)
79 Output
->OpenDescriptor(STDOUT_FILENO
, FileFd::WriteOnly
, false);
84 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
85 ConfigToDoHashes(DoHashes
, "APT::FTPArchive");
88 // FTWScanner::Scanner - FTW Scanner /*{{{*/
89 // ---------------------------------------------------------------------
90 /* This is the FTW scanner, it processes each directory element in the
92 int FTWScanner::ScannerFTW(const char *File
,const struct stat
* /*sb*/,int Flag
)
97 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
102 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
107 return ScannerFile(File
, true);
110 // FTWScanner::ScannerFile - File Scanner /*{{{*/
111 // ---------------------------------------------------------------------
113 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
115 const char *LastComponent
= strrchr(File
, '/');
116 char *RealPath
= NULL
;
118 if (LastComponent
== NULL
)
119 LastComponent
= File
;
123 vector
<string
>::const_iterator I
;
124 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
126 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
129 if (I
== Owner
->Patterns
.end())
132 /* Process it. If the file is a link then resolve it into an absolute
133 name.. This works best if the directory components the scanner are
134 given are not links themselves. */
136 Owner
->OriginalPath
= File
;
138 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
139 (RealPath
= realpath(File
,NULL
)) != 0)
141 Owner
->DoPackage(RealPath
);
145 Owner
->DoPackage(File
);
147 if (_error
->empty() == false)
149 // Print any errors or warnings found
151 bool SeenPath
= false;
152 while (_error
->empty() == false)
156 bool const Type
= _error
->PopMessage(Err
);
158 cerr
<< _("E: ") << Err
<< endl
;
160 cerr
<< _("W: ") << Err
<< endl
;
162 if (Err
.find(File
) != string::npos
)
166 if (SeenPath
== false)
167 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
174 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
175 // ---------------------------------------------------------------------
177 bool FTWScanner::RecursiveScan(string
const &Dir
)
179 char *RealPath
= NULL
;
180 /* If noprefix is set then jam the scan root in, so we don't generate
181 link followed paths out of control */
182 if (InternalPrefix
.empty() == true)
184 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
185 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
186 InternalPrefix
= RealPath
;
190 // Do recursive directory searching
192 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
194 // Error treewalking?
197 if (_error
->PendingError() == false)
198 _error
->Errno("ftw",_("Tree walking failed"));
205 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
206 // ---------------------------------------------------------------------
207 /* This is an alternative to using FTW to locate files, it reads the list
208 of files from another file. */
209 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
211 char *RealPath
= NULL
;
212 /* If noprefix is set then jam the scan root in, so we don't generate
213 link followed paths out of control */
214 if (InternalPrefix
.empty() == true)
216 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
217 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
218 InternalPrefix
= RealPath
;
223 FILE *List
= fopen(File
.c_str(),"r");
225 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
227 /* We are a tad tricky here.. We prefix the buffer with the directory
228 name, that way if we need a full path with just use line.. Sneaky and
232 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
233 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
235 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
236 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
238 char *FileName
= _strstrip(FileStart
);
239 if (FileName
[0] == 0)
242 if (FileName
[0] != '/')
244 if (FileName
!= FileStart
)
245 memmove(FileStart
,FileName
,strlen(FileStart
));
252 if (stat(FileName
,&St
) != 0)
256 if (ScannerFile(FileName
, false) != 0)
264 // FTWScanner::Delink - Delink symlinks /*{{{*/
265 // ---------------------------------------------------------------------
267 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
268 unsigned long long &DeLinkBytes
,
269 unsigned long long const &FileSize
)
271 // See if this isn't an internaly prefix'd file name.
272 if (InternalPrefix
.empty() == false &&
273 InternalPrefix
.length() < FileName
.length() &&
274 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
275 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
277 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
279 // Tidy up the display
280 if (DeLinkBytes
== 0)
284 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
285 SizeToStr(FileSize
).c_str());
288 if (NoLinkAct
== false)
291 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
292 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
295 if (unlink(OriginalPath
) != 0)
296 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
299 if (link(FileName
.c_str(),OriginalPath
) != 0)
301 // Panic! Restore the symlink
302 if (symlink(OldLink
,OriginalPath
) != 0)
303 _error
->Errno("symlink", "failed to restore symlink");
304 return _error
->Errno("link",_("*** Failed to link %s to %s"),
312 DeLinkBytes
+= FileSize
;
313 if (DeLinkBytes
/1024 >= DeLinkLimit
)
314 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
317 FileName
= OriginalPath
;
324 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
325 // ---------------------------------------------------------------------
327 PackagesWriter::PackagesWriter(FileFd
* const GivenOutput
, string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
328 string
const &Arch
) :
329 FTWScanner(GivenOutput
, Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
331 SetExts(".deb .udeb");
334 // Process the command line options
335 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Packages");
336 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
337 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
338 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
339 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
341 if (Db
.Loaded() == false)
344 // Read the override file
345 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
350 if (ExtOverrides
.empty() == false)
351 Over
.ReadExtraOverride(ExtOverrides
);
353 _error
->DumpErrors();
356 // FTWScanner::SetExts - Set extensions to support /*{{{*/
357 // ---------------------------------------------------------------------
359 bool FTWScanner::SetExts(string
const &Vals
)
362 string::size_type Start
= 0;
363 while (Start
<= Vals
.length()-1)
365 string::size_type
const Space
= Vals
.find(' ',Start
);
366 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
367 if ( Arch
.empty() == false )
369 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
370 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
373 AddPattern(string("*") + Vals
.substr(Start
, Length
));
382 // PackagesWriter::DoPackage - Process a single package /*{{{*/
383 // ---------------------------------------------------------------------
384 /* This method takes a package and gets its control information and
385 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
386 rewritten and the path/size/hash appended. */
387 bool PackagesWriter::DoPackage(string FileName
)
389 // Pull all the data we need form the DB
390 if (Db
.GetFileInfo(FileName
,
391 true, /* DoControl */
393 true, /* GenContentsOnly */
394 false, /* DoSource */
395 DoHashes
, DoAlwaysStat
) == false)
400 unsigned long long FileSize
= Db
.GetFileSize();
401 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
404 // Lookup the overide information
405 pkgTagSection
&Tags
= Db
.Control
.Section
;
406 string Package
= Tags
.FindS("Package");
408 // if we generate a Packages file for a given arch, we use it to
409 // look for overrides. if we run in "simple" mode without the
410 // "Architecures" variable in the config we use the architecure value
415 Architecture
= Tags
.FindS("Architecture");
416 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
418 if (Package
.empty() == true)
419 return _error
->Error(_("Archive had no package field"));
421 // If we need to do any rewriting of the header do it now..
422 if (OverItem
.get() == 0)
424 if (NoOverride
== false)
427 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
430 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
431 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
432 OverItem
->Priority
= Tags
.FindS("Priority");
435 // Strip the DirStrip prefix from the FileName and add the PathPrefix
437 if (DirStrip
.empty() == false &&
438 FileName
.length() > DirStrip
.length() &&
439 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
440 DirStrip
.begin(),DirStrip
.end()) == 0)
441 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
443 NewFileName
= FileName
;
444 if (PathPrefix
.empty() == false)
445 NewFileName
= flCombine(PathPrefix
,NewFileName
);
447 /* Configuration says we don't want to include the long Description
448 in the package file - instead we want to ship a separated file */
450 if (LongDescription
== false) {
451 desc
= Tags
.FindS("Description").append("\n");
452 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
455 // This lists all the changes to the fields we are going to make.
456 std::vector
<pkgTagSection::Tag
> Changes
;
459 strprintf(Size
, "%llu", (unsigned long long) FileSize
);
460 Changes
.push_back(pkgTagSection::Tag::Rewrite("Size", Size
));
462 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
464 if (hs
->HashType() == "MD5Sum")
465 Changes
.push_back(pkgTagSection::Tag::Rewrite("MD5sum", hs
->HashValue()));
466 else if (hs
->HashType() == "Checksum-FileSize")
469 Changes
.push_back(pkgTagSection::Tag::Rewrite(hs
->HashType(), hs
->HashValue()));
471 Changes
.push_back(pkgTagSection::Tag::Rewrite("Filename", NewFileName
));
472 Changes
.push_back(pkgTagSection::Tag::Rewrite("Priority", OverItem
->Priority
));
473 Changes
.push_back(pkgTagSection::Tag::Remove("Status"));
474 Changes
.push_back(pkgTagSection::Tag::Remove("Optional"));
476 string DescriptionMd5
;
477 if (LongDescription
== false) {
478 MD5Summation descmd5
;
479 descmd5
.Add(desc
.c_str());
480 DescriptionMd5
= descmd5
.Result().Value();
481 Changes
.push_back(pkgTagSection::Tag::Rewrite("Description-md5", DescriptionMd5
));
482 if (TransWriter
!= NULL
)
483 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
486 // Rewrite the maintainer field if necessary
488 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
489 if (MaintFailed
== true)
491 if (NoOverride
== false)
494 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
495 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
499 if (NewMaint
.empty() == false)
500 Changes
.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint
));
502 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
503 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
504 but dpkg does this append bit. So we do the append bit, at least that way the
505 status file and package file will remain similar. There are other transforms
506 but optional is the only legacy one still in use for some lazy reason. */
507 string OptionalStr
= Tags
.FindS("Optional");
508 if (OptionalStr
.empty() == false)
510 if (Tags
.FindS("Suggests").empty() == false)
511 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
512 Changes
.push_back(pkgTagSection::Tag::Rewrite("Suggests", OptionalStr
));
515 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
516 I
!= OverItem
->FieldOverride
.end(); ++I
)
517 Changes
.push_back(pkgTagSection::Tag::Rewrite(I
->first
, I
->second
));
519 // Rewrite and store the fields.
520 if (Tags
.Write(*Output
, TFRewritePackageOrder
, Changes
) == false ||
521 Output
->Write("\n", 1) == false)
528 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
529 // ---------------------------------------------------------------------
530 /* Create a Translation-Master file for this Packages file */
531 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
532 mode_t
const &Permissions
) : RefCounter(0)
534 if (File
.empty() == true)
537 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
538 Output
= &Comp
->Input
;
541 // TranslationWriter::DoPackage - Process a single package /*{{{*/
542 // ---------------------------------------------------------------------
543 /* Create a Translation-Master file for this Packages file */
544 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
550 // Different archs can include different versions and therefore
551 // different descriptions - so we need to check for both name and md5.
552 string
const Record
= Pkg
+ ":" + MD5
;
554 if (Included
.find(Record
) != Included
.end())
558 strprintf(out
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
559 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
560 Output
->Write(out
.c_str(), out
.length());
562 Included
.insert(Record
);
566 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
567 // ---------------------------------------------------------------------
569 TranslationWriter::~TranslationWriter()
578 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
579 // ---------------------------------------------------------------------
581 SourcesWriter::SourcesWriter(FileFd
* const GivenOutput
, string
const &DB
, string
const &BOverrides
,string
const &SOverrides
,
582 string
const &ExtOverrides
) :
583 FTWScanner(GivenOutput
), Db(DB
), Stats(Db
.Stats
)
590 // Process the command line options
591 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Sources");
592 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
593 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
595 // Read the override file
596 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
601 // WTF?? The logic above: if we can't read binary overrides, don't even try
602 // reading source overrides. if we can read binary overrides, then say there
603 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
605 if (ExtOverrides
.empty() == false)
606 SOver
.ReadExtraOverride(ExtOverrides
);
608 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
609 SOver
.ReadOverride(SOverrides
,true);
612 // SourcesWriter::DoPackage - Process a single package /*{{{*/
613 static std::string
getDscHash(unsigned int const DoHashes
,
614 Hashes::SupportedHashes
const DoIt
, pkgTagSection
&Tags
, char const * const FieldName
,
615 HashString
const * const Hash
, unsigned long long Size
, std::string FileName
)
617 if ((DoHashes
& DoIt
) != DoIt
|| Tags
.Exists(FieldName
) == false || Hash
== NULL
)
619 std::ostringstream out
;
620 out
<< "\n " << Hash
->HashValue() << " " << Size
<< " " << FileName
621 << "\n " << Tags
.FindS(FieldName
);
624 bool SourcesWriter::DoPackage(string FileName
)
626 // Pull all the data we need form the DB
627 if (Db
.GetFileInfo(FileName
,
628 false, /* DoControl */
629 false, /* DoContents */
630 false, /* GenContentsOnly */
632 DoHashes
, DoAlwaysStat
) == false)
637 // we need to perform a "write" here (this is what finish is doing)
638 // because the call to Db.GetFileInfo() in the loop will change
643 if (Tags
.Scan(Db
.Dsc
.Data
.c_str(), Db
.Dsc
.Data
.length()) == false)
644 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
646 if (Tags
.Exists("Source") == false)
647 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
650 // Lookup the overide information, finding first the best priority.
652 string Bins
= Tags
.FindS("Binary");
653 char Buffer
[Bins
.length() + 1];
654 auto_ptr
<Override::Item
> OverItem(0);
655 if (Bins
.empty() == false)
657 strcpy(Buffer
,Bins
.c_str());
659 // Ignore too-long errors.
661 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
663 // Look at all the binaries
664 unsigned char BestPrioV
= pkgCache::State::Extra
;
665 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
667 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
671 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
672 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
674 BestPrioV
= NewPrioV
;
675 BestPrio
= Itm
->Priority
;
678 if (OverItem
.get() == 0)
683 // If we need to do any rewriting of the header do it now..
684 if (OverItem
.get() == 0)
686 if (NoOverride
== false)
689 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
692 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
696 if (stat(FileName
.c_str(), &St
) != 0)
697 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
699 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
700 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
701 if (SOverItem
.get() == 0)
703 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
704 SOverItem
= auto_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
705 if (SOverItem
.get() == 0)
707 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
708 SOverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
709 *SOverItem
= *OverItem
;
713 // Add the dsc to the files hash list
714 string
const strippedName
= flNotDir(FileName
);
715 std::string
const Files
= getDscHash(DoHashes
, Hashes::MD5SUM
, Tags
, "Files", Db
.HashesList
.find("MD5Sum"), St
.st_size
, strippedName
);
716 std::string ChecksumsSha1
= getDscHash(DoHashes
, Hashes::SHA1SUM
, Tags
, "Checksums-Sha1", Db
.HashesList
.find("SHA1"), St
.st_size
, strippedName
);
717 std::string ChecksumsSha256
= getDscHash(DoHashes
, Hashes::SHA256SUM
, Tags
, "Checksums-Sha256", Db
.HashesList
.find("SHA256"), St
.st_size
, strippedName
);
718 std::string ChecksumsSha512
= getDscHash(DoHashes
, Hashes::SHA512SUM
, Tags
, "Checksums-Sha512", Db
.HashesList
.find("SHA512"), St
.st_size
, strippedName
);
720 // Strip the DirStrip prefix from the FileName and add the PathPrefix
722 if (DirStrip
.empty() == false &&
723 FileName
.length() > DirStrip
.length() &&
724 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
725 NewFileName
= string(OriginalPath
+ DirStrip
.length());
727 NewFileName
= OriginalPath
;
728 if (PathPrefix
.empty() == false)
729 NewFileName
= flCombine(PathPrefix
,NewFileName
);
731 string Directory
= flNotFile(OriginalPath
);
732 string Package
= Tags
.FindS("Source");
734 // Perform operation over all of the files
736 const char *C
= Files
.c_str();
737 char *RealPath
= NULL
;
738 for (;isspace(*C
); C
++);
741 // Parse each of the elements
742 if (ParseQuoteWord(C
,ParseJnk
) == false ||
743 ParseQuoteWord(C
,ParseJnk
) == false ||
744 ParseQuoteWord(C
,ParseJnk
) == false)
745 return _error
->Error("Error parsing file record");
747 string OriginalPath
= Directory
+ ParseJnk
;
749 // Add missing hashes to source files
750 if (((DoHashes
& Hashes::SHA1SUM
) == Hashes::SHA1SUM
&& !Tags
.Exists("Checksums-Sha1")) ||
751 ((DoHashes
& Hashes::SHA256SUM
) == Hashes::SHA256SUM
&& !Tags
.Exists("Checksums-Sha256")) ||
752 ((DoHashes
& Hashes::SHA512SUM
) == Hashes::SHA512SUM
&& !Tags
.Exists("Checksums-Sha512")))
754 if (Db
.GetFileInfo(OriginalPath
,
755 false, /* DoControl */
756 false, /* DoContents */
757 false, /* GenContentsOnly */
758 false, /* DoSource */
760 DoAlwaysStat
) == false)
762 return _error
->Error("Error getting file info");
765 for (HashStringList::const_iterator hs
= Db
.HashesList
.begin(); hs
!= Db
.HashesList
.end(); ++hs
)
767 if (hs
->HashType() == "MD5Sum" || hs
->HashType() == "Checksum-FileSize")
769 char const * fieldname
;
771 if (hs
->HashType() == "SHA1")
773 fieldname
= "Checksums-Sha1";
774 out
= &ChecksumsSha1
;
776 else if (hs
->HashType() == "SHA256")
778 fieldname
= "Checksums-Sha256";
779 out
= &ChecksumsSha256
;
781 else if (hs
->HashType() == "SHA512")
783 fieldname
= "Checksums-Sha512";
784 out
= &ChecksumsSha512
;
788 _error
->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs
->HashType().c_str());
791 if (Tags
.Exists(fieldname
) == true)
793 std::ostringstream streamout
;
794 streamout
<< "\n " << hs
->HashValue() << " " << Db
.GetFileSize() << " " << ParseJnk
;
795 out
->append(streamout
.str());
798 // write back the GetFileInfo() stats data
802 // Perform the delinking operation
805 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
806 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
808 string RP
= RealPath
;
810 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
815 Directory
= flNotFile(NewFileName
);
816 if (Directory
.length() > 2)
817 Directory
.erase(Directory
.end()-1);
819 // This lists all the changes to the fields we are going to make.
820 // (5 hardcoded + checksums + maintainer + end marker)
821 std::vector
<pkgTagSection::Tag
> Changes
;
823 Changes
.push_back(pkgTagSection::Tag::Remove("Source"));
824 Changes
.push_back(pkgTagSection::Tag::Rewrite("Package", Package
));
825 if (Files
.empty() == false)
826 Changes
.push_back(pkgTagSection::Tag::Rewrite("Files", Files
));
827 if (ChecksumsSha1
.empty() == false)
828 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha1", ChecksumsSha1
));
829 if (ChecksumsSha256
.empty() == false)
830 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha256", ChecksumsSha256
));
831 if (ChecksumsSha512
.empty() == false)
832 Changes
.push_back(pkgTagSection::Tag::Rewrite("Checksums-Sha512", ChecksumsSha512
));
833 if (Directory
!= "./")
834 Changes
.push_back(pkgTagSection::Tag::Rewrite("Directory", Directory
));
835 Changes
.push_back(pkgTagSection::Tag::Rewrite("Priority", BestPrio
));
836 Changes
.push_back(pkgTagSection::Tag::Remove("Status"));
838 // Rewrite the maintainer field if necessary
840 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"), MaintFailed
);
841 if (MaintFailed
== true)
843 if (NoOverride
== false)
846 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
847 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
850 if (NewMaint
.empty() == false)
851 Changes
.push_back(pkgTagSection::Tag::Rewrite("Maintainer", NewMaint
.c_str()));
853 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
854 I
!= SOverItem
->FieldOverride
.end(); ++I
)
855 Changes
.push_back(pkgTagSection::Tag::Rewrite(I
->first
, I
->second
));
857 // Rewrite and store the fields.
858 if (Tags
.Write(*Output
, TFRewriteSourceOrder
, Changes
) == false ||
859 Output
->Write("\n", 1) == false)
868 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
869 // ---------------------------------------------------------------------
871 ContentsWriter::ContentsWriter(FileFd
* const GivenOutput
, string
const &DB
, string
const &Arch
) :
872 FTWScanner(GivenOutput
, Arch
), Db(DB
), Stats(Db
.Stats
)
878 // ContentsWriter::DoPackage - Process a single package /*{{{*/
879 // ---------------------------------------------------------------------
880 /* If Package is the empty string the control record will be parsed to
881 determine what the package name is. */
882 bool ContentsWriter::DoPackage(string FileName
, string Package
)
884 if (!Db
.GetFileInfo(FileName
,
885 Package
.empty(), /* DoControl */
886 true, /* DoContents */
887 false, /* GenContentsOnly */
888 false, /* DoSource */
890 false /* checkMtime */))
895 // Parse the package name
896 if (Package
.empty() == true)
898 Package
= Db
.Control
.Section
.FindS("Package");
901 Db
.Contents
.Add(Gen
,Package
);
906 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
907 // ---------------------------------------------------------------------
909 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
911 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
912 if (_error
->PendingError() == true)
915 // Open the package file
917 if (Pkgs
.OpenOld(Fd
) == false)
920 pkgTagFile
Tags(&Fd
);
921 if (_error
->PendingError() == true)
925 pkgTagSection Section
;
926 while (Tags
.Step(Section
) == true)
928 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
929 string Package
= Section
.FindS("Section");
930 if (Package
.empty() == false && Package
.end()[-1] != '/')
933 Package
+= Section
.FindS("Package");
936 Package
+= Section
.FindS("Package");
938 DoPackage(File
,Package
);
939 if (_error
->empty() == false)
941 _error
->Error("Errors apply to file '%s'",File
.c_str());
942 _error
->DumpErrors();
946 // Tidy the compressor
954 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
955 // ---------------------------------------------------------------------
957 ReleaseWriter::ReleaseWriter(FileFd
* const GivenOutput
, string
const &/*DB*/) : FTWScanner(GivenOutput
)
959 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
961 AddPattern("Packages");
962 AddPattern("Packages.gz");
963 AddPattern("Packages.bz2");
964 AddPattern("Packages.lzma");
965 AddPattern("Packages.xz");
966 AddPattern("Translation-*");
967 AddPattern("Sources");
968 AddPattern("Sources.gz");
969 AddPattern("Sources.bz2");
970 AddPattern("Sources.lzma");
971 AddPattern("Sources.xz");
972 AddPattern("Release");
973 AddPattern("Contents-*");
975 AddPattern("md5sum.txt");
977 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
979 time_t const now
= time(NULL
);
981 setlocale(LC_TIME
, "C");
984 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
990 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
992 if (now
== validuntil
||
993 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
994 gmtime(&validuntil
)) == 0)
999 setlocale(LC_TIME
, "");
1001 map
<string
,string
> Fields
;
1002 Fields
["Origin"] = "";
1003 Fields
["Label"] = "";
1004 Fields
["Suite"] = "";
1005 Fields
["Version"] = "";
1006 Fields
["Codename"] = "";
1007 Fields
["Date"] = datestr
;
1008 Fields
["Valid-Until"] = validstr
;
1009 Fields
["Architectures"] = "";
1010 Fields
["Components"] = "";
1011 Fields
["Description"] = "";
1013 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1017 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1018 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1022 std::string
const out
= I
->first
+ ": " + Value
+ "\n";
1023 Output
->Write(out
.c_str(), out
.length());
1026 ConfigToDoHashes(DoHashes
, "APT::FTPArchive::Release");
1029 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1030 // ---------------------------------------------------------------------
1031 bool ReleaseWriter::DoPackage(string FileName
)
1033 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1035 if (DirStrip
.empty() == false &&
1036 FileName
.length() > DirStrip
.length() &&
1037 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1038 DirStrip
.begin(),DirStrip
.end()) == 0)
1040 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1041 while (NewFileName
[0] == '/')
1042 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1045 NewFileName
= FileName
;
1047 if (PathPrefix
.empty() == false)
1048 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1050 FileFd
fd(FileName
, FileFd::ReadOnly
);
1057 CheckSums
[NewFileName
].size
= fd
.Size();
1059 Hashes
hs(DoHashes
);
1061 CheckSums
[NewFileName
].Hashes
= hs
.GetHashStringList();
1068 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1069 // ---------------------------------------------------------------------
1070 static void printChecksumTypeRecord(FileFd
&Output
, char const * const Type
, map
<string
, ReleaseWriter::CheckSum
> const &CheckSums
)
1074 strprintf(out
, "%s:\n", Type
);
1075 Output
.Write(out
.c_str(), out
.length());
1077 for(map
<string
,ReleaseWriter::CheckSum
>::const_iterator I
= CheckSums
.begin();
1078 I
!= CheckSums
.end(); ++I
)
1080 HashString
const * const hs
= I
->second
.Hashes
.find(Type
);
1084 strprintf(out
, " %s %16llu %s\n",
1085 hs
->HashValue().c_str(),
1087 (*I
).first
.c_str());
1088 Output
.Write(out
.c_str(), out
.length());
1091 void ReleaseWriter::Finish()
1093 if ((DoHashes
& Hashes::MD5SUM
) == Hashes::MD5SUM
)
1094 printChecksumTypeRecord(*Output
, "MD5Sum", CheckSums
);
1095 if ((DoHashes
& Hashes::SHA1SUM
) == Hashes::SHA1SUM
)
1096 printChecksumTypeRecord(*Output
, "SHA1", CheckSums
);
1097 if ((DoHashes
& Hashes::SHA256SUM
) == Hashes::SHA256SUM
)
1098 printChecksumTypeRecord(*Output
, "SHA256", CheckSums
);
1099 if ((DoHashes
& Hashes::SHA512SUM
) == Hashes::SHA512SUM
)
1100 printChecksumTypeRecord(*Output
, "SHA512", CheckSums
);