1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
4 /* ######################################################################
8 The file writer classes. These write various types of output, sources,
11 ##################################################################### */
13 // Include Files /*{{{*/
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/error.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/md5.h>
21 #include <apt-pkg/sha1.h>
22 #include <apt-pkg/sha256.h>
23 #include <apt-pkg/deblistparser.h>
25 #include <sys/types.h>
35 #include "apt-ftparchive.h"
36 #include "multicompress.h"
39 FTWScanner
*FTWScanner::Owner
;
41 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
42 // ---------------------------------------------------------------------
44 inline void SetTFRewriteData(struct TFRewriteData
&tfrd
,
47 const char *newtag
= 0)
50 tfrd
.Rewrite
= rewrite
;
55 // FTWScanner::FTWScanner - Constructor /*{{{*/
56 // ---------------------------------------------------------------------
58 FTWScanner::FTWScanner(string
const &Arch
): Arch(Arch
)
61 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
63 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
64 DoSHA1
= _config
->FindB("APT::FTPArchive::SHA1",true);
65 DoSHA256
= _config
->FindB("APT::FTPArchive::SHA256",true);
68 // FTWScanner::Scanner - FTW Scanner /*{{{*/
69 // ---------------------------------------------------------------------
70 /* This is the FTW scanner, it processes each directory element in the
72 int FTWScanner::ScannerFTW(const char *File
,const struct stat
*sb
,int Flag
)
77 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
82 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
87 return ScannerFile(File
, true);
90 // FTWScanner::ScannerFile - File Scanner /*{{{*/
91 // ---------------------------------------------------------------------
93 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
95 const char *LastComponent
= strrchr(File
, '/');
96 char *RealPath
= NULL
;
98 if (LastComponent
== NULL
)
103 vector
<string
>::const_iterator I
;
104 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
106 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
109 if (I
== Owner
->Patterns
.end())
112 /* Process it. If the file is a link then resolve it into an absolute
113 name.. This works best if the directory components the scanner are
114 given are not links themselves. */
116 Owner
->OriginalPath
= File
;
118 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
119 (RealPath
= realpath(File
,NULL
)) != 0)
121 Owner
->DoPackage(RealPath
);
125 Owner
->DoPackage(File
);
127 if (_error
->empty() == false)
129 // Print any errors or warnings found
131 bool SeenPath
= false;
132 while (_error
->empty() == false)
136 bool const Type
= _error
->PopMessage(Err
);
138 cerr
<< _("E: ") << Err
<< endl
;
140 cerr
<< _("W: ") << Err
<< endl
;
142 if (Err
.find(File
) != string::npos
)
146 if (SeenPath
== false)
147 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
154 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
155 // ---------------------------------------------------------------------
157 bool FTWScanner::RecursiveScan(string
const &Dir
)
159 char *RealPath
= NULL
;
160 /* If noprefix is set then jam the scan root in, so we don't generate
161 link followed paths out of control */
162 if (InternalPrefix
.empty() == true)
164 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
165 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
166 InternalPrefix
= RealPath
;
170 // Do recursive directory searching
172 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
174 // Error treewalking?
177 if (_error
->PendingError() == false)
178 _error
->Errno("ftw",_("Tree walking failed"));
185 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
186 // ---------------------------------------------------------------------
187 /* This is an alternative to using FTW to locate files, it reads the list
188 of files from another file. */
189 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
191 char *RealPath
= NULL
;
192 /* If noprefix is set then jam the scan root in, so we don't generate
193 link followed paths out of control */
194 if (InternalPrefix
.empty() == true)
196 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
197 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
198 InternalPrefix
= RealPath
;
203 FILE *List
= fopen(File
.c_str(),"r");
205 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
207 /* We are a tad tricky here.. We prefix the buffer with the directory
208 name, that way if we need a full path with just use line.. Sneaky and
212 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
213 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
215 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
216 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
218 char *FileName
= _strstrip(FileStart
);
219 if (FileName
[0] == 0)
222 if (FileName
[0] != '/')
224 if (FileName
!= FileStart
)
225 memmove(FileStart
,FileName
,strlen(FileStart
));
232 if (stat(FileName
,&St
) != 0)
236 if (ScannerFile(FileName
, false) != 0)
244 // FTWScanner::Delink - Delink symlinks /*{{{*/
245 // ---------------------------------------------------------------------
247 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
248 unsigned long &DeLinkBytes
,
249 off_t
const &FileSize
)
251 // See if this isn't an internaly prefix'd file name.
252 if (InternalPrefix
.empty() == false &&
253 InternalPrefix
.length() < FileName
.length() &&
254 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
255 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
257 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
259 // Tidy up the display
260 if (DeLinkBytes
== 0)
264 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
265 SizeToStr(FileSize
).c_str());
268 if (NoLinkAct
== false)
271 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
272 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
275 if (unlink(OriginalPath
) != 0)
276 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
279 if (link(FileName
.c_str(),OriginalPath
) != 0)
281 // Panic! Restore the symlink
282 symlink(OldLink
,OriginalPath
);
283 return _error
->Errno("link",_("*** Failed to link %s to %s"),
291 DeLinkBytes
+= FileSize
;
292 if (DeLinkBytes
/1024 >= DeLinkLimit
)
293 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
296 FileName
= OriginalPath
;
303 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
304 // ---------------------------------------------------------------------
306 PackagesWriter::PackagesWriter(string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
307 string
const &Arch
) :
308 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
311 SetExts(".deb .udeb");
314 // Process the command line options
315 DoMD5
= _config
->FindB("APT::FTPArchive::Packages::MD5",DoMD5
);
316 DoSHA1
= _config
->FindB("APT::FTPArchive::Packages::SHA1",DoSHA1
);
317 DoSHA256
= _config
->FindB("APT::FTPArchive::Packages::SHA256",DoSHA256
);
318 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
319 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
320 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
321 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
323 if (Db
.Loaded() == false)
326 // Read the override file
327 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
332 if (ExtOverrides
.empty() == false)
333 Over
.ReadExtraOverride(ExtOverrides
);
335 _error
->DumpErrors();
338 // FTWScanner::SetExts - Set extensions to support /*{{{*/
339 // ---------------------------------------------------------------------
341 bool FTWScanner::SetExts(string
const &Vals
)
344 string::size_type Start
= 0;
345 while (Start
<= Vals
.length()-1)
347 string::size_type
const Space
= Vals
.find(' ',Start
);
348 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
349 if ( Arch
.empty() == false )
351 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
352 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
355 AddPattern(string("*") + Vals
.substr(Start
, Length
));
364 // PackagesWriter::DoPackage - Process a single package /*{{{*/
365 // ---------------------------------------------------------------------
366 /* This method takes a package and gets its control information and
367 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
368 rewritten and the path/size/hash appended. */
369 bool PackagesWriter::DoPackage(string FileName
)
371 // Pull all the data we need form the DB
372 if (Db
.GetFileInfo(FileName
, true, DoContents
, true, DoMD5
, DoSHA1
, DoSHA256
, DoAlwaysStat
)
378 off_t FileSize
= Db
.GetFileSize();
379 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
382 // Lookup the overide information
383 pkgTagSection
&Tags
= Db
.Control
.Section
;
384 string Package
= Tags
.FindS("Package");
386 // if we generate a Packages file for a given arch, we use it to
387 // look for overrides. if we run in "simple" mode without the
388 // "Architecures" variable in the config we use the architecure value
393 Architecture
= Tags
.FindS("Architecture");
394 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
396 if (Package
.empty() == true)
397 return _error
->Error(_("Archive had no package field"));
399 // If we need to do any rewriting of the header do it now..
400 if (OverItem
.get() == 0)
402 if (NoOverride
== false)
405 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
408 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
409 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
410 OverItem
->Priority
= Tags
.FindS("Priority");
414 sprintf(Size
,"%lu", (unsigned long) FileSize
);
416 // Strip the DirStrip prefix from the FileName and add the PathPrefix
418 if (DirStrip
.empty() == false &&
419 FileName
.length() > DirStrip
.length() &&
420 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
421 DirStrip
.begin(),DirStrip
.end()) == 0)
422 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
424 NewFileName
= FileName
;
425 if (PathPrefix
.empty() == false)
426 NewFileName
= flCombine(PathPrefix
,NewFileName
);
428 /* Configuration says we don't want to include the long Description
429 in the package file - instead we want to ship a separated file */
431 if (LongDescription
== false) {
432 desc
= Tags
.FindS("Description").append("\n");
433 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
436 // This lists all the changes to the fields we are going to make.
437 // (7 hardcoded + maintainer + suggests + end marker)
438 TFRewriteData Changes
[6+2+OverItem
->FieldOverride
.size()+1+1];
440 unsigned int End
= 0;
441 SetTFRewriteData(Changes
[End
++], "Size", Size
);
443 SetTFRewriteData(Changes
[End
++], "MD5sum", Db
.MD5Res
.c_str());
445 SetTFRewriteData(Changes
[End
++], "SHA1", Db
.SHA1Res
.c_str());
446 if (DoSHA256
== true)
447 SetTFRewriteData(Changes
[End
++], "SHA256", Db
.SHA256Res
.c_str());
448 SetTFRewriteData(Changes
[End
++], "Filename", NewFileName
.c_str());
449 SetTFRewriteData(Changes
[End
++], "Priority", OverItem
->Priority
.c_str());
450 SetTFRewriteData(Changes
[End
++], "Status", 0);
451 SetTFRewriteData(Changes
[End
++], "Optional", 0);
453 string DescriptionMd5
;
454 if (LongDescription
== false) {
455 MD5Summation descmd5
;
456 descmd5
.Add(desc
.c_str());
457 DescriptionMd5
= descmd5
.Result().Value();
458 SetTFRewriteData(Changes
[End
++], "Description-md5", DescriptionMd5
.c_str());
459 if (TransWriter
!= NULL
)
460 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
463 // Rewrite the maintainer field if necessary
465 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
466 if (MaintFailed
== true)
468 if (NoOverride
== false)
471 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
472 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
476 if (NewMaint
.empty() == false)
477 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
479 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
480 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
481 but dpkg does this append bit. So we do the append bit, at least that way the
482 status file and package file will remain similar. There are other transforms
483 but optional is the only legacy one still in use for some lazy reason. */
484 string OptionalStr
= Tags
.FindS("Optional");
485 if (OptionalStr
.empty() == false)
487 if (Tags
.FindS("Suggests").empty() == false)
488 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
489 SetTFRewriteData(Changes
[End
++], "Suggests", OptionalStr
.c_str());
492 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
493 I
!= OverItem
->FieldOverride
.end(); I
++)
494 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
496 SetTFRewriteData(Changes
[End
++], 0, 0);
498 // Rewrite and store the fields.
499 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
) == false)
501 fprintf(Output
,"\n");
507 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
508 // ---------------------------------------------------------------------
509 /* Create a Translation-Master file for this Packages file */
510 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
511 mode_t
const &Permissions
) : Output(NULL
),
514 if (File
.empty() == true)
517 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
518 Output
= Comp
->Input
;
521 // TranslationWriter::DoPackage - Process a single package /*{{{*/
522 // ---------------------------------------------------------------------
523 /* Create a Translation-Master file for this Packages file */
524 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
530 // Different archs can include different versions and therefore
531 // different descriptions - so we need to check for both name and md5.
532 string
const Record
= Pkg
+ ":" + MD5
;
534 if (Included
.find(Record
) != Included
.end())
537 fprintf(Output
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
538 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
540 Included
.insert(Record
);
544 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
545 // ---------------------------------------------------------------------
547 TranslationWriter::~TranslationWriter()
556 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
557 // ---------------------------------------------------------------------
559 SourcesWriter::SourcesWriter(string
const &BOverrides
,string
const &SOverrides
,
560 string
const &ExtOverrides
)
568 // Process the command line options
569 DoMD5
= _config
->FindB("APT::FTPArchive::Sources::MD5",DoMD5
);
570 DoSHA1
= _config
->FindB("APT::FTPArchive::Sources::SHA1",DoSHA1
);
571 DoSHA256
= _config
->FindB("APT::FTPArchive::Sources::SHA256",DoSHA256
);
572 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
574 // Read the override file
575 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
580 // WTF?? The logic above: if we can't read binary overrides, don't even try
581 // reading source overrides. if we can read binary overrides, then say there
582 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
584 if (ExtOverrides
.empty() == false)
585 SOver
.ReadExtraOverride(ExtOverrides
);
587 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
588 SOver
.ReadOverride(SOverrides
,true);
591 // SourcesWriter::DoPackage - Process a single package /*{{{*/
592 // ---------------------------------------------------------------------
594 bool SourcesWriter::DoPackage(string FileName
)
597 FileFd
F(FileName
,FileFd::ReadOnly
);
598 if (_error
->PendingError() == true)
601 // Stat the file for later
603 if (fstat(F
.Fd(),&St
) != 0)
604 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
606 if (St
.st_size
> 128*1024)
607 return _error
->Error("DSC file '%s' is too large!",FileName
.c_str());
609 if (BufSize
< (unsigned)St
.st_size
+1)
611 BufSize
= St
.st_size
+1;
612 Buffer
= (char *)realloc(Buffer
,St
.st_size
+1);
615 if (F
.Read(Buffer
,St
.st_size
) == false)
619 char *Start
= Buffer
;
620 char *BlkEnd
= Buffer
+ St
.st_size
;
624 SHA256Summation SHA256
;
627 MD5
.Add((unsigned char *)Start
,BlkEnd
- Start
);
629 SHA1
.Add((unsigned char *)Start
,BlkEnd
- Start
);
630 if (DoSHA256
== true)
631 SHA256
.Add((unsigned char *)Start
,BlkEnd
- Start
);
633 // Add an extra \n to the end, just in case
636 /* Remove the PGP trailer. Some .dsc's have this without a blank line
638 const char *Key
= "-----BEGIN PGP SIGNATURE-----";
639 for (char *MsgEnd
= Start
; MsgEnd
< BlkEnd
- strlen(Key
) -1; MsgEnd
++)
641 if (*MsgEnd
== '\n' && strncmp(MsgEnd
+1,Key
,strlen(Key
)) == 0)
648 /* Read records until we locate the Source record. This neatly skips the
649 GPG header (which is RFC822 formed) without any trouble. */
654 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
655 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
656 if (Tags
.Find("Source",Pos
) == true)
658 Start
+= Tags
.size();
663 // Lookup the overide information, finding first the best priority.
665 string Bins
= Tags
.FindS("Binary");
666 char Buffer
[Bins
.length() + 1];
667 auto_ptr
<Override::Item
> OverItem(0);
668 if (Bins
.empty() == false)
670 strcpy(Buffer
,Bins
.c_str());
672 // Ignore too-long errors.
674 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
676 // Look at all the binaries
677 unsigned char BestPrioV
= pkgCache::State::Extra
;
678 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
680 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
684 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
685 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
687 BestPrioV
= NewPrioV
;
688 BestPrio
= Itm
->Priority
;
691 if (OverItem
.get() == 0)
696 // If we need to do any rewriting of the header do it now..
697 if (OverItem
.get() == 0)
699 if (NoOverride
== false)
702 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
705 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
708 auto_ptr
<Override::Item
> SOverItem(SOver
.GetItem(Tags
.FindS("Source")));
709 // const auto_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
= auto_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
= auto_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::ostringstream ostreamFiles
;
725 if (DoMD5
== true && Tags
.Exists("Files"))
726 ostreamFiles
<< "\n " << string(MD5
.Result()) << " " << St
.st_size
<< " "
727 << strippedName
<< "\n " << Tags
.FindS("Files");
728 string
const Files
= ostreamFiles
.str();
730 std::ostringstream ostreamSha1
;
731 if (DoSHA1
== true && Tags
.Exists("Checksums-Sha1"))
732 ostreamSha1
<< "\n " << string(SHA1
.Result()) << " " << St
.st_size
<< " "
733 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha1");
734 string
const ChecksumsSha1
= ostreamSha1
.str();
736 std::ostringstream ostreamSha256
;
737 if (DoSHA256
== true && Tags
.Exists("Checksums-Sha256"))
738 ostreamSha256
<< "\n " << string(SHA256
.Result()) << " " << St
.st_size
<< " "
739 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha256");
740 string
const ChecksumsSha256
= ostreamSha256
.str();
742 // Strip the DirStrip prefix from the FileName and add the PathPrefix
744 if (DirStrip
.empty() == false &&
745 FileName
.length() > DirStrip
.length() &&
746 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
747 NewFileName
= string(OriginalPath
+ DirStrip
.length());
749 NewFileName
= OriginalPath
;
750 if (PathPrefix
.empty() == false)
751 NewFileName
= flCombine(PathPrefix
,NewFileName
);
753 string Directory
= flNotFile(OriginalPath
);
754 string Package
= Tags
.FindS("Source");
756 // Perform the delinking operation over all of the files
758 const char *C
= Files
.c_str();
759 char *RealPath
= NULL
;
760 for (;isspace(*C
); C
++);
763 // Parse each of the elements
764 if (ParseQuoteWord(C
,ParseJnk
) == false ||
765 ParseQuoteWord(C
,ParseJnk
) == false ||
766 ParseQuoteWord(C
,ParseJnk
) == false)
767 return _error
->Error("Error parsing file record");
770 string OriginalPath
= Directory
+ ParseJnk
;
771 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
772 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
774 string RP
= RealPath
;
776 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
781 Directory
= flNotFile(NewFileName
);
782 if (Directory
.length() > 2)
783 Directory
.erase(Directory
.end()-1);
785 // This lists all the changes to the fields we are going to make.
786 // (5 hardcoded + checksums + maintainer + end marker)
787 TFRewriteData Changes
[5+2+1+SOverItem
->FieldOverride
.size()+1];
789 unsigned int End
= 0;
790 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
791 if (Files
.empty() == false)
792 SetTFRewriteData(Changes
[End
++],"Files",Files
.c_str());
793 if (ChecksumsSha1
.empty() == false)
794 SetTFRewriteData(Changes
[End
++],"Checksums-Sha1",ChecksumsSha1
.c_str());
795 if (ChecksumsSha256
.empty() == false)
796 SetTFRewriteData(Changes
[End
++],"Checksums-Sha256",ChecksumsSha256
.c_str());
797 if (Directory
!= "./")
798 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
799 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
800 SetTFRewriteData(Changes
[End
++],"Status",0);
802 // Rewrite the maintainer field if necessary
804 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
805 if (MaintFailed
== true)
807 if (NoOverride
== false)
810 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
811 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
814 if (NewMaint
.empty() == false)
815 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
817 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
818 I
!= SOverItem
->FieldOverride
.end(); I
++)
819 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
821 SetTFRewriteData(Changes
[End
++], 0, 0);
823 // Rewrite and store the fields.
824 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
826 fprintf(Output
,"\n");
834 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
835 // ---------------------------------------------------------------------
837 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
838 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
845 // ContentsWriter::DoPackage - Process a single package /*{{{*/
846 // ---------------------------------------------------------------------
847 /* If Package is the empty string the control record will be parsed to
848 determine what the package name is. */
849 bool ContentsWriter::DoPackage(string FileName
, string Package
)
851 if (!Db
.GetFileInfo(FileName
, Package
.empty(), true, false, false, false, false, false))
856 // Parse the package name
857 if (Package
.empty() == true)
859 Package
= Db
.Control
.Section
.FindS("Package");
862 Db
.Contents
.Add(Gen
,Package
);
867 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
868 // ---------------------------------------------------------------------
870 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
872 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
873 if (_error
->PendingError() == true)
876 // Open the package file
879 if (Pkgs
.OpenOld(CompFd
,Proc
) == false)
883 FileFd
Fd(CompFd
,false);
884 pkgTagFile
Tags(&Fd
);
885 if (_error
->PendingError() == true)
887 Pkgs
.CloseOld(CompFd
,Proc
);
892 pkgTagSection Section
;
893 while (Tags
.Step(Section
) == true)
895 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
896 string Package
= Section
.FindS("Section");
897 if (Package
.empty() == false && Package
.end()[-1] != '/')
900 Package
+= Section
.FindS("Package");
903 Package
+= Section
.FindS("Package");
905 DoPackage(File
,Package
);
906 if (_error
->empty() == false)
908 _error
->Error("Errors apply to file '%s'",File
.c_str());
909 _error
->DumpErrors();
913 // Tidy the compressor
914 if (Pkgs
.CloseOld(CompFd
,Proc
) == false)
922 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
923 // ---------------------------------------------------------------------
925 ReleaseWriter::ReleaseWriter(string
const &DB
)
927 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
929 AddPattern("Packages");
930 AddPattern("Packages.gz");
931 AddPattern("Packages.bz2");
932 AddPattern("Packages.lzma");
933 AddPattern("Packages.xz");
934 AddPattern("Sources");
935 AddPattern("Sources.gz");
936 AddPattern("Sources.bz2");
937 AddPattern("Sources.lzma");
938 AddPattern("Sources.xz");
939 AddPattern("Release");
941 AddPattern("md5sum.txt");
943 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
946 time_t const now
= time(NULL
);
948 setlocale(LC_TIME
, "C");
951 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
957 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
959 if (now
== validuntil
||
960 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
961 gmtime(&validuntil
)) == 0)
966 setlocale(LC_TIME
, "");
968 map
<string
,string
> Fields
;
969 Fields
["Origin"] = "";
970 Fields
["Label"] = "";
971 Fields
["Suite"] = "";
972 Fields
["Version"] = "";
973 Fields
["Codename"] = "";
974 Fields
["Date"] = datestr
;
975 Fields
["Valid-Until"] = validstr
;
976 Fields
["Architectures"] = "";
977 Fields
["Components"] = "";
978 Fields
["Description"] = "";
980 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
984 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
985 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
989 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
992 DoMD5
= _config
->FindB("APT::FTPArchive::Release::MD5",DoMD5
);
993 DoSHA1
= _config
->FindB("APT::FTPArchive::Release::SHA1",DoSHA1
);
994 DoSHA256
= _config
->FindB("APT::FTPArchive::Release::SHA256",DoSHA256
);
997 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
998 // ---------------------------------------------------------------------
999 bool ReleaseWriter::DoPackage(string FileName
)
1001 // Strip the DirStrip prefix from the FileName and add the PathPrefix
1003 if (DirStrip
.empty() == false &&
1004 FileName
.length() > DirStrip
.length() &&
1005 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
1006 DirStrip
.begin(),DirStrip
.end()) == 0)
1008 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
1009 while (NewFileName
[0] == '/')
1010 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
1013 NewFileName
= FileName
;
1015 if (PathPrefix
.empty() == false)
1016 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1018 FileFd
fd(FileName
, FileFd::ReadOnly
);
1025 CheckSums
[NewFileName
].size
= fd
.Size();
1030 MD5
.AddFD(fd
.Fd(), fd
.Size());
1031 CheckSums
[NewFileName
].MD5
= MD5
.Result();
1037 SHA1
.AddFD(fd
.Fd(), fd
.Size());
1038 CheckSums
[NewFileName
].SHA1
= SHA1
.Result();
1041 if (DoSHA256
== true)
1043 SHA256Summation SHA256
;
1044 SHA256
.AddFD(fd
.Fd(), fd
.Size());
1045 CheckSums
[NewFileName
].SHA256
= SHA256
.Result();
1054 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1055 // ---------------------------------------------------------------------
1056 void ReleaseWriter::Finish()
1060 fprintf(Output
, "MD5Sum:\n");
1061 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1062 I
!= CheckSums
.end(); ++I
)
1064 fprintf(Output
, " %s %16ld %s\n",
1065 (*I
).second
.MD5
.c_str(),
1067 (*I
).first
.c_str());
1072 fprintf(Output
, "SHA1:\n");
1073 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1074 I
!= CheckSums
.end(); ++I
)
1076 fprintf(Output
, " %s %16ld %s\n",
1077 (*I
).second
.SHA1
.c_str(),
1079 (*I
).first
.c_str());
1082 if (DoSHA256
== true)
1084 fprintf(Output
, "SHA256:\n");
1085 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1086 I
!= CheckSums
.end(); ++I
)
1088 fprintf(Output
, " %s %16ld %s\n",
1089 (*I
).second
.SHA256
.c_str(),
1091 (*I
).first
.c_str());