1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
4 /* ######################################################################
8 The file writer classes. These write various types of output, sources,
11 ##################################################################### */
13 // Include Files /*{{{*/
16 #include <apt-pkg/configuration.h>
17 #include <apt-pkg/deblistparser.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/gpgv.h>
21 #include <apt-pkg/hashes.h>
22 #include <apt-pkg/md5.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/debfile.h>
25 #include <apt-pkg/pkgcache.h>
26 #include <apt-pkg/sha1.h>
27 #include <apt-pkg/sha2.h>
28 #include <apt-pkg/tagfile.h>
36 #include <sys/types.h>
44 #include "apt-ftparchive.h"
47 #include "multicompress.h"
52 FTWScanner
*FTWScanner::Owner
;
54 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
55 // ---------------------------------------------------------------------
57 static inline TFRewriteData
SetTFRewriteData(const char *tag
,
59 const char *newtag
= 0)
63 tfrd
.Rewrite
= rewrite
;
69 // FTWScanner::FTWScanner - Constructor /*{{{*/
70 // ---------------------------------------------------------------------
72 FTWScanner::FTWScanner(string
const &Arch
): Arch(Arch
)
75 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
77 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
78 DoSHA1
= _config
->FindB("APT::FTPArchive::SHA1",true);
79 DoSHA256
= _config
->FindB("APT::FTPArchive::SHA256",true);
80 DoSHA512
= _config
->FindB("APT::FTPArchive::SHA512",true);
83 // FTWScanner::Scanner - FTW Scanner /*{{{*/
84 // ---------------------------------------------------------------------
85 /* This is the FTW scanner, it processes each directory element in the
87 int FTWScanner::ScannerFTW(const char *File
,const struct stat
* /*sb*/,int Flag
)
92 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
97 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
102 return ScannerFile(File
, true);
105 // FTWScanner::ScannerFile - File Scanner /*{{{*/
106 // ---------------------------------------------------------------------
108 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
110 const char *LastComponent
= strrchr(File
, '/');
111 char *RealPath
= NULL
;
113 if (LastComponent
== NULL
)
114 LastComponent
= File
;
118 vector
<string
>::const_iterator I
;
119 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
121 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
124 if (I
== Owner
->Patterns
.end())
127 /* Process it. If the file is a link then resolve it into an absolute
128 name.. This works best if the directory components the scanner are
129 given are not links themselves. */
131 Owner
->OriginalPath
= File
;
133 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
134 (RealPath
= realpath(File
,NULL
)) != 0)
136 Owner
->DoPackage(RealPath
);
140 Owner
->DoPackage(File
);
142 if (_error
->empty() == false)
144 // Print any errors or warnings found
146 bool SeenPath
= false;
147 while (_error
->empty() == false)
151 bool const Type
= _error
->PopMessage(Err
);
153 cerr
<< _("E: ") << Err
<< endl
;
155 cerr
<< _("W: ") << Err
<< endl
;
157 if (Err
.find(File
) != string::npos
)
161 if (SeenPath
== false)
162 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
169 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
170 // ---------------------------------------------------------------------
172 bool FTWScanner::RecursiveScan(string
const &Dir
)
174 char *RealPath
= NULL
;
175 /* If noprefix is set then jam the scan root in, so we don't generate
176 link followed paths out of control */
177 if (InternalPrefix
.empty() == true)
179 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
180 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
181 InternalPrefix
= RealPath
;
185 // Do recursive directory searching
187 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
189 // Error treewalking?
192 if (_error
->PendingError() == false)
193 _error
->Errno("ftw",_("Tree walking failed"));
200 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
201 // ---------------------------------------------------------------------
202 /* This is an alternative to using FTW to locate files, it reads the list
203 of files from another file. */
204 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
206 char *RealPath
= NULL
;
207 /* If noprefix is set then jam the scan root in, so we don't generate
208 link followed paths out of control */
209 if (InternalPrefix
.empty() == true)
211 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
212 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
213 InternalPrefix
= RealPath
;
218 FILE *List
= fopen(File
.c_str(),"r");
220 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
222 /* We are a tad tricky here.. We prefix the buffer with the directory
223 name, that way if we need a full path with just use line.. Sneaky and
227 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
228 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
230 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
231 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
233 char *FileName
= _strstrip(FileStart
);
234 if (FileName
[0] == 0)
237 if (FileName
[0] != '/')
239 if (FileName
!= FileStart
)
240 memmove(FileStart
,FileName
,strlen(FileStart
));
247 if (stat(FileName
,&St
) != 0)
251 if (ScannerFile(FileName
, false) != 0)
259 // FTWScanner::Delink - Delink symlinks /*{{{*/
260 // ---------------------------------------------------------------------
262 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
263 unsigned long long &DeLinkBytes
,
264 unsigned long long const &FileSize
)
266 // See if this isn't an internaly prefix'd file name.
267 if (InternalPrefix
.empty() == false &&
268 InternalPrefix
.length() < FileName
.length() &&
269 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
270 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
272 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
274 // Tidy up the display
275 if (DeLinkBytes
== 0)
279 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
280 SizeToStr(FileSize
).c_str());
283 if (NoLinkAct
== false)
286 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
287 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
290 if (unlink(OriginalPath
) != 0)
291 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
294 if (link(FileName
.c_str(),OriginalPath
) != 0)
296 // Panic! Restore the symlink
297 if (symlink(OldLink
,OriginalPath
) != 0)
298 _error
->Errno("symlink", "failed to restore symlink");
299 return _error
->Errno("link",_("*** Failed to link %s to %s"),
307 DeLinkBytes
+= FileSize
;
308 if (DeLinkBytes
/1024 >= DeLinkLimit
)
309 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
312 FileName
= OriginalPath
;
319 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
320 // ---------------------------------------------------------------------
322 PackagesWriter::PackagesWriter(string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
323 string
const &Arch
) :
324 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
327 SetExts(".deb .udeb");
330 // Process the command line options
331 DoMD5
= _config
->FindB("APT::FTPArchive::Packages::MD5",DoMD5
);
332 DoSHA1
= _config
->FindB("APT::FTPArchive::Packages::SHA1",DoSHA1
);
333 DoSHA256
= _config
->FindB("APT::FTPArchive::Packages::SHA256",DoSHA256
);
334 DoSHA512
= _config
->FindB("APT::FTPArchive::Packages::SHA512",DoSHA512
);
335 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
336 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
337 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
338 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
340 if (Db
.Loaded() == false)
343 // Read the override file
344 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
349 if (ExtOverrides
.empty() == false)
350 Over
.ReadExtraOverride(ExtOverrides
);
352 _error
->DumpErrors();
355 // FTWScanner::SetExts - Set extensions to support /*{{{*/
356 // ---------------------------------------------------------------------
358 bool FTWScanner::SetExts(string
const &Vals
)
361 string::size_type Start
= 0;
362 while (Start
<= Vals
.length()-1)
364 string::size_type
const Space
= Vals
.find(' ',Start
);
365 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
366 if ( Arch
.empty() == false )
368 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
369 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
372 AddPattern(string("*") + Vals
.substr(Start
, Length
));
381 // PackagesWriter::DoPackage - Process a single package /*{{{*/
382 // ---------------------------------------------------------------------
383 /* This method takes a package and gets its control information and
384 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
385 rewritten and the path/size/hash appended. */
386 bool PackagesWriter::DoPackage(string FileName
)
388 // Pull all the data we need form the DB
389 if (Db
.GetFileInfo(FileName
,
390 true, /* DoControl */
392 true, /* GenContentsOnly */
393 false, /* DoSource */
394 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
, DoAlwaysStat
) == false)
399 unsigned long long FileSize
= Db
.GetFileSize();
400 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
403 // Lookup the overide information
404 pkgTagSection
&Tags
= Db
.Control
.Section
;
405 string Package
= Tags
.FindS("Package");
407 // if we generate a Packages file for a given arch, we use it to
408 // look for overrides. if we run in "simple" mode without the
409 // "Architecures" variable in the config we use the architecure value
414 Architecture
= Tags
.FindS("Architecture");
415 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
417 if (Package
.empty() == true)
418 return _error
->Error(_("Archive had no package field"));
420 // If we need to do any rewriting of the header do it now..
421 if (OverItem
.get() == 0)
423 if (NoOverride
== false)
426 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
429 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
430 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
431 OverItem
->Priority
= Tags
.FindS("Priority");
435 sprintf(Size
,"%llu", (unsigned long long) FileSize
);
437 // Strip the DirStrip prefix from the FileName and add the PathPrefix
439 if (DirStrip
.empty() == false &&
440 FileName
.length() > DirStrip
.length() &&
441 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
442 DirStrip
.begin(),DirStrip
.end()) == 0)
443 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
445 NewFileName
= FileName
;
446 if (PathPrefix
.empty() == false)
447 NewFileName
= flCombine(PathPrefix
,NewFileName
);
449 /* Configuration says we don't want to include the long Description
450 in the package file - instead we want to ship a separated file */
452 if (LongDescription
== false) {
453 desc
= Tags
.FindS("Description").append("\n");
454 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
457 // This lists all the changes to the fields we are going to make.
458 std::vector
<TFRewriteData
> Changes
;
460 Changes
.push_back(SetTFRewriteData("Size", Size
));
462 Changes
.push_back(SetTFRewriteData("MD5sum", Db
.MD5Res
.c_str()));
464 Changes
.push_back(SetTFRewriteData("SHA1", Db
.SHA1Res
.c_str()));
465 if (DoSHA256
== true)
466 Changes
.push_back(SetTFRewriteData("SHA256", Db
.SHA256Res
.c_str()));
467 if (DoSHA512
== true)
468 Changes
.push_back(SetTFRewriteData("SHA512", Db
.SHA512Res
.c_str()));
469 Changes
.push_back(SetTFRewriteData("Filename", NewFileName
.c_str()));
470 Changes
.push_back(SetTFRewriteData("Priority", OverItem
->Priority
.c_str()));
471 Changes
.push_back(SetTFRewriteData("Status", 0));
472 Changes
.push_back(SetTFRewriteData("Optional", 0));
474 string DescriptionMd5
;
475 if (LongDescription
== false) {
476 MD5Summation descmd5
;
477 descmd5
.Add(desc
.c_str());
478 DescriptionMd5
= descmd5
.Result().Value();
479 Changes
.push_back(SetTFRewriteData("Description-md5", DescriptionMd5
.c_str()));
480 if (TransWriter
!= NULL
)
481 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
484 // Rewrite the maintainer field if necessary
486 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
487 if (MaintFailed
== true)
489 if (NoOverride
== false)
492 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
493 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
497 if (NewMaint
.empty() == false)
498 Changes
.push_back(SetTFRewriteData("Maintainer", NewMaint
.c_str()));
500 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
501 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
502 but dpkg does this append bit. So we do the append bit, at least that way the
503 status file and package file will remain similar. There are other transforms
504 but optional is the only legacy one still in use for some lazy reason. */
505 string OptionalStr
= Tags
.FindS("Optional");
506 if (OptionalStr
.empty() == false)
508 if (Tags
.FindS("Suggests").empty() == false)
509 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
510 Changes
.push_back(SetTFRewriteData("Suggests", OptionalStr
.c_str()));
513 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
514 I
!= OverItem
->FieldOverride
.end(); ++I
)
515 Changes
.push_back(SetTFRewriteData(I
->first
.c_str(),I
->second
.c_str()));
517 Changes
.push_back(SetTFRewriteData( 0, 0));
519 // Rewrite and store the fields.
520 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
.data()) == false)
522 fprintf(Output
,"\n");
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
) : Output(NULL
),
535 if (File
.empty() == true)
538 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
539 Output
= Comp
->Input
;
542 // TranslationWriter::DoPackage - Process a single package /*{{{*/
543 // ---------------------------------------------------------------------
544 /* Create a Translation-Master file for this Packages file */
545 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
551 // Different archs can include different versions and therefore
552 // different descriptions - so we need to check for both name and md5.
553 string
const Record
= Pkg
+ ":" + MD5
;
555 if (Included
.find(Record
) != Included
.end())
558 fprintf(Output
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
559 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
561 Included
.insert(Record
);
565 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
566 // ---------------------------------------------------------------------
568 TranslationWriter::~TranslationWriter()
577 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
578 // ---------------------------------------------------------------------
580 SourcesWriter::SourcesWriter(string
const &DB
, string
const &BOverrides
,string
const &SOverrides
,
581 string
const &ExtOverrides
) :
582 Db(DB
), Stats(Db
.Stats
)
590 // Process the command line options
591 DoMD5
= _config
->FindB("APT::FTPArchive::Sources::MD5",DoMD5
);
592 DoSHA1
= _config
->FindB("APT::FTPArchive::Sources::SHA1",DoSHA1
);
593 DoSHA256
= _config
->FindB("APT::FTPArchive::Sources::SHA256",DoSHA256
);
594 DoSHA512
= _config
->FindB("APT::FTPArchive::Sources::SHA512",DoSHA512
);
595 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
596 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
598 // Read the override file
599 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
604 // WTF?? The logic above: if we can't read binary overrides, don't even try
605 // reading source overrides. if we can read binary overrides, then say there
606 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
608 if (ExtOverrides
.empty() == false)
609 SOver
.ReadExtraOverride(ExtOverrides
);
611 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
612 SOver
.ReadOverride(SOverrides
,true);
615 // SourcesWriter::DoPackage - Process a single package /*{{{*/
616 // ---------------------------------------------------------------------
618 bool SourcesWriter::DoPackage(string FileName
)
620 // Pull all the data we need form the DB
621 if (Db
.GetFileInfo(FileName
,
622 false, /* DoControl */
623 false, /* DoContents */
624 false, /* GenContentsOnly */
626 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
, DoAlwaysStat
) == false)
631 // we need to perform a "write" here (this is what finish is doing)
632 // because the call to Db.GetFileInfo() in the loop will change
637 char *Start
= Db
.Dsc
.Data
;
638 char *BlkEnd
= Db
.Dsc
.Data
+ Db
.Dsc
.Length
;
640 // Add extra \n to the end, just in case (as in clearsigned they are missing)
645 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
646 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
648 if (Tags
.Exists("Source") == false)
649 return _error
->Error("Could not find a Source entry in the DSC '%s'",FileName
.c_str());
652 // Lookup the overide information, finding first the best priority.
654 string Bins
= Tags
.FindS("Binary");
655 char Buffer
[Bins
.length() + 1];
656 auto_ptr
<Override::Item
> OverItem(0);
657 if (Bins
.empty() == false)
659 strcpy(Buffer
,Bins
.c_str());
661 // Ignore too-long errors.
663 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
665 // Look at all the binaries
666 unsigned char BestPrioV
= pkgCache::State::Extra
;
667 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
669 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
673 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
674 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
676 BestPrioV
= NewPrioV
;
677 BestPrio
= Itm
->Priority
;
680 if (OverItem
.get() == 0)
685 // If we need to do any rewriting of the header do it now..
686 if (OverItem
.get() == 0)
688 if (NoOverride
== false)
691 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
694 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
698 if (stat(FileName
.c_str(), &St
) != 0)
699 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
701 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
702 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
703 if (SOverItem
.get() == 0)
705 ioprintf(c1out
, _(" %s has no source override entry\n"), Tags
.FindS("Source").c_str());
706 SOverItem
= auto_ptr
<Override::Item
>(BOver
.GetItem(Tags
.FindS("Source")));
707 if (SOverItem
.get() == 0)
709 ioprintf(c1out
, _(" %s has no binary override entry either\n"), Tags
.FindS("Source").c_str());
710 SOverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
711 *SOverItem
= *OverItem
;
715 // Add the dsc to the files hash list
716 string
const strippedName
= flNotDir(FileName
);
717 std::ostringstream ostreamFiles
;
718 if (DoMD5
== true && Tags
.Exists("Files"))
719 ostreamFiles
<< "\n " << Db
.MD5Res
.c_str() << " " << St
.st_size
<< " "
720 << strippedName
<< "\n " << Tags
.FindS("Files");
721 string
const Files
= ostreamFiles
.str();
723 std::ostringstream ostreamSha1
;
724 if (DoSHA1
== true && Tags
.Exists("Checksums-Sha1"))
725 ostreamSha1
<< "\n " << string(Db
.SHA1Res
.c_str()) << " " << St
.st_size
<< " "
726 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha1");
728 std::ostringstream ostreamSha256
;
729 if (DoSHA256
== true && Tags
.Exists("Checksums-Sha256"))
730 ostreamSha256
<< "\n " << string(Db
.SHA256Res
.c_str()) << " " << St
.st_size
<< " "
731 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha256");
733 std::ostringstream ostreamSha512
;
734 if (DoSHA512
== true && Tags
.Exists("Checksums-Sha512"))
735 ostreamSha512
<< "\n " << string(Db
.SHA512Res
.c_str()) << " " << St
.st_size
<< " "
736 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha512");
738 // Strip the DirStrip prefix from the FileName and add the PathPrefix
740 if (DirStrip
.empty() == false &&
741 FileName
.length() > DirStrip
.length() &&
742 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
743 NewFileName
= string(OriginalPath
+ DirStrip
.length());
745 NewFileName
= OriginalPath
;
746 if (PathPrefix
.empty() == false)
747 NewFileName
= flCombine(PathPrefix
,NewFileName
);
749 string Directory
= flNotFile(OriginalPath
);
750 string Package
= Tags
.FindS("Source");
752 // Perform operation over all of the files
754 const char *C
= Files
.c_str();
755 char *RealPath
= NULL
;
756 for (;isspace(*C
); C
++);
759 // Parse each of the elements
760 if (ParseQuoteWord(C
,ParseJnk
) == false ||
761 ParseQuoteWord(C
,ParseJnk
) == false ||
762 ParseQuoteWord(C
,ParseJnk
) == false)
763 return _error
->Error("Error parsing file record");
765 string OriginalPath
= Directory
+ ParseJnk
;
767 // Add missing hashes to source files
768 if ((DoSHA1
== true && !Tags
.Exists("Checksums-Sha1")) ||
769 (DoSHA256
== true && !Tags
.Exists("Checksums-Sha256")) ||
770 (DoSHA512
== true && !Tags
.Exists("Checksums-Sha512")))
772 if (Db
.GetFileInfo(OriginalPath
,
773 false, /* DoControl */
774 false, /* DoContents */
775 false, /* GenContentsOnly */
776 false, /* DoSource */
777 DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
,
778 DoAlwaysStat
) == false)
780 return _error
->Error("Error getting file info");
783 if (DoSHA1
== true && !Tags
.Exists("Checksums-Sha1"))
784 ostreamSha1
<< "\n " << string(Db
.SHA1Res
) << " "
785 << Db
.GetFileSize() << " " << ParseJnk
;
787 if (DoSHA256
== true && !Tags
.Exists("Checksums-Sha256"))
788 ostreamSha256
<< "\n " << string(Db
.SHA256Res
) << " "
789 << Db
.GetFileSize() << " " << ParseJnk
;
791 if (DoSHA512
== true && !Tags
.Exists("Checksums-Sha512"))
792 ostreamSha512
<< "\n " << string(Db
.SHA512Res
) << " "
793 << Db
.GetFileSize() << " " << ParseJnk
;
795 // write back the GetFileInfo() stats data
799 // Perform the delinking operation
802 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
803 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
805 string RP
= RealPath
;
807 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
812 Directory
= flNotFile(NewFileName
);
813 if (Directory
.length() > 2)
814 Directory
.erase(Directory
.end()-1);
816 string
const ChecksumsSha1
= ostreamSha1
.str();
817 string
const ChecksumsSha256
= ostreamSha256
.str();
818 string
const ChecksumsSha512
= ostreamSha512
.str();
820 // This lists all the changes to the fields we are going to make.
821 // (5 hardcoded + checksums + maintainer + end marker)
822 std::vector
<TFRewriteData
> Changes
;
824 Changes
.push_back(SetTFRewriteData("Source",Package
.c_str(),"Package"));
825 if (Files
.empty() == false)
826 Changes
.push_back(SetTFRewriteData("Files",Files
.c_str()));
827 if (ChecksumsSha1
.empty() == false)
828 Changes
.push_back(SetTFRewriteData("Checksums-Sha1",ChecksumsSha1
.c_str()));
829 if (ChecksumsSha256
.empty() == false)
830 Changes
.push_back(SetTFRewriteData("Checksums-Sha256",ChecksumsSha256
.c_str()));
831 if (ChecksumsSha512
.empty() == false)
832 Changes
.push_back(SetTFRewriteData("Checksums-Sha512",ChecksumsSha512
.c_str()));
833 if (Directory
!= "./")
834 Changes
.push_back(SetTFRewriteData("Directory",Directory
.c_str()));
835 Changes
.push_back(SetTFRewriteData("Priority",BestPrio
.c_str()));
836 Changes
.push_back(SetTFRewriteData("Status",0));
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(SetTFRewriteData("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(SetTFRewriteData(I
->first
.c_str(),I
->second
.c_str()));
857 Changes
.push_back(SetTFRewriteData(0, 0));
859 // Rewrite and store the fields.
860 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
.data()) == false)
862 fprintf(Output
,"\n");
870 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
871 // ---------------------------------------------------------------------
873 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
874 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
881 // ContentsWriter::DoPackage - Process a single package /*{{{*/
882 // ---------------------------------------------------------------------
883 /* If Package is the empty string the control record will be parsed to
884 determine what the package name is. */
885 bool ContentsWriter::DoPackage(string FileName
, string Package
)
887 if (!Db
.GetFileInfo(FileName
,
888 Package
.empty(), /* DoControl */
889 true, /* DoContents */
890 false, /* GenContentsOnly */
891 false, /* DoSource */
894 false, /* DoSHA256 */
895 false)) /* DoSHA512 */
900 // Parse the package name
901 if (Package
.empty() == true)
903 Package
= Db
.Control
.Section
.FindS("Package");
906 Db
.Contents
.Add(Gen
,Package
);
911 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
912 // ---------------------------------------------------------------------
914 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
916 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
917 if (_error
->PendingError() == true)
920 // Open the package file
922 if (Pkgs
.OpenOld(Fd
) == false)
925 pkgTagFile
Tags(&Fd
);
926 if (_error
->PendingError() == true)
930 pkgTagSection Section
;
931 while (Tags
.Step(Section
) == true)
933 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
934 string Package
= Section
.FindS("Section");
935 if (Package
.empty() == false && Package
.end()[-1] != '/')
938 Package
+= Section
.FindS("Package");
941 Package
+= Section
.FindS("Package");
943 DoPackage(File
,Package
);
944 if (_error
->empty() == false)
946 _error
->Error("Errors apply to file '%s'",File
.c_str());
947 _error
->DumpErrors();
951 // Tidy the compressor
959 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
960 // ---------------------------------------------------------------------
962 ReleaseWriter::ReleaseWriter(string
const &/*DB*/)
964 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
966 AddPattern("Packages");
967 AddPattern("Packages.gz");
968 AddPattern("Packages.bz2");
969 AddPattern("Packages.lzma");
970 AddPattern("Packages.xz");
971 AddPattern("Translation-*");
972 AddPattern("Sources");
973 AddPattern("Sources.gz");
974 AddPattern("Sources.bz2");
975 AddPattern("Sources.lzma");
976 AddPattern("Sources.xz");
977 AddPattern("Release");
978 AddPattern("Contents-*");
980 AddPattern("md5sum.txt");
982 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
985 time_t const now
= time(NULL
);
987 setlocale(LC_TIME
, "C");
990 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
996 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
998 if (now
== validuntil
||
999 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
1000 gmtime(&validuntil
)) == 0)
1005 setlocale(LC_TIME
, "");
1007 map
<string
,string
> Fields
;
1008 Fields
["Origin"] = "";
1009 Fields
["Label"] = "";
1010 Fields
["Suite"] = "";
1011 Fields
["Version"] = "";
1012 Fields
["Codename"] = "";
1013 Fields
["Date"] = datestr
;
1014 Fields
["Valid-Until"] = validstr
;
1015 Fields
["Architectures"] = "";
1016 Fields
["Components"] = "";
1017 Fields
["Description"] = "";
1019 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
1023 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
1024 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
1028 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
1031 DoMD5
= _config
->FindB("APT::FTPArchive::Release::MD5",DoMD5
);
1032 DoSHA1
= _config
->FindB("APT::FTPArchive::Release::SHA1",DoSHA1
);
1033 DoSHA256
= _config
->FindB("APT::FTPArchive::Release::SHA256",DoSHA256
);
1036 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
1037 // ---------------------------------------------------------------------
1038 bool ReleaseWriter::DoPackage(string FileName
)
1040 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1042 if (DirStrip
.empty() == false &&
1043 FileName
.length() > DirStrip
.length() &&
1044 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1045 DirStrip
.begin(),DirStrip
.end()) == 0)
1047 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1048 while (NewFileName
[0] == '/')
1049 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1052 NewFileName
= FileName
;
1054 if (PathPrefix
.empty() == false)
1055 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1057 FileFd
fd(FileName
, FileFd::ReadOnly
);
1064 CheckSums
[NewFileName
].size
= fd
.Size();
1067 hs
.AddFD(fd
, 0, DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
);
1069 CheckSums
[NewFileName
].MD5
= hs
.MD5
.Result();
1071 CheckSums
[NewFileName
].SHA1
= hs
.SHA1
.Result();
1072 if (DoSHA256
== true)
1073 CheckSums
[NewFileName
].SHA256
= hs
.SHA256
.Result();
1074 if (DoSHA512
== true)
1075 CheckSums
[NewFileName
].SHA512
= hs
.SHA512
.Result();
1082 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1083 // ---------------------------------------------------------------------
1084 void ReleaseWriter::Finish()
1088 fprintf(Output
, "MD5Sum:\n");
1089 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1090 I
!= CheckSums
.end(); ++I
)
1092 fprintf(Output
, " %s %16llu %s\n",
1093 (*I
).second
.MD5
.c_str(),
1095 (*I
).first
.c_str());
1100 fprintf(Output
, "SHA1:\n");
1101 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1102 I
!= CheckSums
.end(); ++I
)
1104 fprintf(Output
, " %s %16llu %s\n",
1105 (*I
).second
.SHA1
.c_str(),
1107 (*I
).first
.c_str());
1110 if (DoSHA256
== true)
1112 fprintf(Output
, "SHA256:\n");
1113 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1114 I
!= CheckSums
.end(); ++I
)
1116 fprintf(Output
, " %s %16llu %s\n",
1117 (*I
).second
.SHA256
.c_str(),
1119 (*I
).first
.c_str());
1123 fprintf(Output
, "SHA512:\n");
1124 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1125 I
!= CheckSums
.end();
1128 fprintf(Output
, " %s %16llu %s\n",
1129 (*I
).second
.SHA512
.c_str(),
1131 (*I
).first
.c_str());