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/sha512.h>
24 #include <apt-pkg/deblistparser.h>
26 #include <sys/types.h>
36 #include "apt-ftparchive.h"
37 #include "multicompress.h"
40 FTWScanner
*FTWScanner::Owner
;
42 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
43 // ---------------------------------------------------------------------
45 inline void SetTFRewriteData(struct TFRewriteData
&tfrd
,
48 const char *newtag
= 0)
51 tfrd
.Rewrite
= rewrite
;
56 // FTWScanner::FTWScanner - Constructor /*{{{*/
57 // ---------------------------------------------------------------------
59 FTWScanner::FTWScanner(string
const &Arch
): Arch(Arch
)
62 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
65 // FTWScanner::Scanner - FTW Scanner /*{{{*/
66 // ---------------------------------------------------------------------
67 /* This is the FTW scanner, it processes each directory element in the
69 int FTWScanner::ScannerFTW(const char *File
,const struct stat
*sb
,int Flag
)
74 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
79 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
84 return ScannerFile(File
, true);
87 // FTWScanner::ScannerFile - File Scanner /*{{{*/
88 // ---------------------------------------------------------------------
90 int FTWScanner::ScannerFile(const char *File
, bool const &ReadLink
)
92 const char *LastComponent
= strrchr(File
, '/');
93 char *RealPath
= NULL
;
95 if (LastComponent
== NULL
)
100 vector
<string
>::const_iterator I
;
101 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
103 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
106 if (I
== Owner
->Patterns
.end())
109 /* Process it. If the file is a link then resolve it into an absolute
110 name.. This works best if the directory components the scanner are
111 given are not links themselves. */
113 Owner
->OriginalPath
= File
;
115 readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
116 (RealPath
= realpath(File
,NULL
)) != 0)
118 Owner
->DoPackage(RealPath
);
122 Owner
->DoPackage(File
);
124 if (_error
->empty() == false)
126 // Print any errors or warnings found
128 bool SeenPath
= false;
129 while (_error
->empty() == false)
133 bool const Type
= _error
->PopMessage(Err
);
135 cerr
<< _("E: ") << Err
<< endl
;
137 cerr
<< _("W: ") << Err
<< endl
;
139 if (Err
.find(File
) != string::npos
)
143 if (SeenPath
== false)
144 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
151 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
152 // ---------------------------------------------------------------------
154 bool FTWScanner::RecursiveScan(string
const &Dir
)
156 char *RealPath
= NULL
;
157 /* If noprefix is set then jam the scan root in, so we don't generate
158 link followed paths out of control */
159 if (InternalPrefix
.empty() == true)
161 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
162 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
163 InternalPrefix
= RealPath
;
167 // Do recursive directory searching
169 int const Res
= ftw(Dir
.c_str(),ScannerFTW
,30);
171 // Error treewalking?
174 if (_error
->PendingError() == false)
175 _error
->Errno("ftw",_("Tree walking failed"));
182 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
183 // ---------------------------------------------------------------------
184 /* This is an alternative to using FTW to locate files, it reads the list
185 of files from another file. */
186 bool FTWScanner::LoadFileList(string
const &Dir
, string
const &File
)
188 char *RealPath
= NULL
;
189 /* If noprefix is set then jam the scan root in, so we don't generate
190 link followed paths out of control */
191 if (InternalPrefix
.empty() == true)
193 if ((RealPath
= realpath(Dir
.c_str(),NULL
)) == 0)
194 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
195 InternalPrefix
= RealPath
;
200 FILE *List
= fopen(File
.c_str(),"r");
202 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
204 /* We are a tad tricky here.. We prefix the buffer with the directory
205 name, that way if we need a full path with just use line.. Sneaky and
209 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
210 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
212 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
213 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
215 char *FileName
= _strstrip(FileStart
);
216 if (FileName
[0] == 0)
219 if (FileName
[0] != '/')
221 if (FileName
!= FileStart
)
222 memmove(FileStart
,FileName
,strlen(FileStart
));
229 if (stat(FileName
,&St
) != 0)
233 if (ScannerFile(FileName
, false) != 0)
241 // FTWScanner::Delink - Delink symlinks /*{{{*/
242 // ---------------------------------------------------------------------
244 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
245 unsigned long &DeLinkBytes
,
246 off_t
const &FileSize
)
248 // See if this isn't an internaly prefix'd file name.
249 if (InternalPrefix
.empty() == false &&
250 InternalPrefix
.length() < FileName
.length() &&
251 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
252 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
254 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
256 // Tidy up the display
257 if (DeLinkBytes
== 0)
261 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
262 SizeToStr(FileSize
).c_str());
265 if (NoLinkAct
== false)
268 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
269 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
272 if (unlink(OriginalPath
) != 0)
273 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
276 if (link(FileName
.c_str(),OriginalPath
) != 0)
278 // Panic! Restore the symlink
279 symlink(OldLink
,OriginalPath
);
280 return _error
->Errno("link",_("*** Failed to link %s to %s"),
288 DeLinkBytes
+= FileSize
;
289 if (DeLinkBytes
/1024 >= DeLinkLimit
)
290 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
293 FileName
= OriginalPath
;
300 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
301 // ---------------------------------------------------------------------
303 PackagesWriter::PackagesWriter(string
const &DB
,string
const &Overrides
,string
const &ExtOverrides
,
304 string
const &Arch
) :
305 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
), TransWriter(NULL
)
308 SetExts(".deb .udeb");
311 // Process the command line options
312 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
313 DoSHA1
= _config
->FindB("APT::FTPArchive::SHA1",true);
314 DoSHA256
= _config
->FindB("APT::FTPArchive::SHA256",true);
315 DoSHA256
= _config
->FindB("APT::FTPArchive::SHA512",true);
316 DoAlwaysStat
= _config
->FindB("APT::FTPArchive::AlwaysStat", false);
317 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
318 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
319 LongDescription
= _config
->FindB("APT::FTPArchive::LongDescription",true);
321 if (Db
.Loaded() == false)
324 // Read the override file
325 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
330 if (ExtOverrides
.empty() == false)
331 Over
.ReadExtraOverride(ExtOverrides
);
333 _error
->DumpErrors();
336 // FTWScanner::SetExts - Set extensions to support /*{{{*/
337 // ---------------------------------------------------------------------
339 bool FTWScanner::SetExts(string
const &Vals
)
342 string::size_type Start
= 0;
343 while (Start
<= Vals
.length()-1)
345 string::size_type
const Space
= Vals
.find(' ',Start
);
346 string::size_type
const Length
= ((Space
== string::npos
) ? Vals
.length() : Space
) - Start
;
347 if ( Arch
.empty() == false )
349 AddPattern(string("*_") + Arch
+ Vals
.substr(Start
, Length
));
350 AddPattern(string("*_all") + Vals
.substr(Start
, Length
));
353 AddPattern(string("*") + Vals
.substr(Start
, Length
));
362 // PackagesWriter::DoPackage - Process a single package /*{{{*/
363 // ---------------------------------------------------------------------
364 /* This method takes a package and gets its control information and
365 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
366 rewritten and the path/size/hash appended. */
367 bool PackagesWriter::DoPackage(string FileName
)
369 // Pull all the data we need form the DB
370 if (Db
.GetFileInfo(FileName
, true, DoContents
, true, DoMD5
, DoSHA1
, DoSHA256
, DoSHA512
, DoAlwaysStat
)
376 off_t FileSize
= Db
.GetFileSize();
377 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,FileSize
) == false)
380 // Lookup the overide information
381 pkgTagSection
&Tags
= Db
.Control
.Section
;
382 string Package
= Tags
.FindS("Package");
384 // if we generate a Packages file for a given arch, we use it to
385 // look for overrides. if we run in "simple" mode without the
386 // "Architecures" variable in the config we use the architecure value
391 Architecture
= Tags
.FindS("Architecture");
392 auto_ptr
<Override::Item
> OverItem(Over
.GetItem(Package
,Architecture
));
394 if (Package
.empty() == true)
395 return _error
->Error(_("Archive had no package field"));
397 // If we need to do any rewriting of the header do it now..
398 if (OverItem
.get() == 0)
400 if (NoOverride
== false)
403 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
406 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
407 OverItem
->FieldOverride
["Section"] = Tags
.FindS("Section");
408 OverItem
->Priority
= Tags
.FindS("Priority");
412 sprintf(Size
,"%lu", (unsigned long) FileSize
);
414 // Strip the DirStrip prefix from the FileName and add the PathPrefix
416 if (DirStrip
.empty() == false &&
417 FileName
.length() > DirStrip
.length() &&
418 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
419 DirStrip
.begin(),DirStrip
.end()) == 0)
420 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
422 NewFileName
= FileName
;
423 if (PathPrefix
.empty() == false)
424 NewFileName
= flCombine(PathPrefix
,NewFileName
);
426 /* Configuration says we don't want to include the long Description
427 in the package file - instead we want to ship a separated file */
429 if (LongDescription
== false) {
430 desc
= Tags
.FindS("Description").append("\n");
431 OverItem
->FieldOverride
["Description"] = desc
.substr(0, desc
.find('\n')).c_str();
434 // This lists all the changes to the fields we are going to make.
435 // (7 hardcoded + maintainer + suggests + end marker)
436 TFRewriteData Changes
[6+2+OverItem
->FieldOverride
.size()+1+1];
438 unsigned int End
= 0;
439 SetTFRewriteData(Changes
[End
++], "Size", Size
);
440 SetTFRewriteData(Changes
[End
++], "MD5sum", Db
.MD5Res
.c_str());
441 SetTFRewriteData(Changes
[End
++], "SHA1", Db
.SHA1Res
.c_str());
442 SetTFRewriteData(Changes
[End
++], "SHA256", Db
.SHA256Res
.c_str());
443 SetTFRewriteData(Changes
[End
++], "SHA512", Db
.SHA512Res
.c_str());
444 SetTFRewriteData(Changes
[End
++], "Filename", NewFileName
.c_str());
445 SetTFRewriteData(Changes
[End
++], "Priority", OverItem
->Priority
.c_str());
446 SetTFRewriteData(Changes
[End
++], "Status", 0);
447 SetTFRewriteData(Changes
[End
++], "Optional", 0);
449 string DescriptionMd5
;
450 if (LongDescription
== false) {
451 MD5Summation descmd5
;
452 descmd5
.Add(desc
.c_str());
453 DescriptionMd5
= descmd5
.Result().Value();
454 SetTFRewriteData(Changes
[End
++], "Description-md5", DescriptionMd5
.c_str());
455 if (TransWriter
!= NULL
)
456 TransWriter
->DoPackage(Package
, desc
, DescriptionMd5
);
459 // Rewrite the maintainer field if necessary
461 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
462 if (MaintFailed
== true)
464 if (NoOverride
== false)
467 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
468 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
472 if (NewMaint
.empty() == false)
473 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
475 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
476 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
477 but dpkg does this append bit. So we do the append bit, at least that way the
478 status file and package file will remain similar. There are other transforms
479 but optional is the only legacy one still in use for some lazy reason. */
480 string OptionalStr
= Tags
.FindS("Optional");
481 if (OptionalStr
.empty() == false)
483 if (Tags
.FindS("Suggests").empty() == false)
484 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
485 SetTFRewriteData(Changes
[End
++], "Suggests", OptionalStr
.c_str());
488 for (map
<string
,string
>::const_iterator I
= OverItem
->FieldOverride
.begin();
489 I
!= OverItem
->FieldOverride
.end(); I
++)
490 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
492 SetTFRewriteData(Changes
[End
++], 0, 0);
494 // Rewrite and store the fields.
495 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
) == false)
497 fprintf(Output
,"\n");
503 // TranslationWriter::TranslationWriter - Constructor /*{{{*/
504 // ---------------------------------------------------------------------
505 /* Create a Translation-Master file for this Packages file */
506 TranslationWriter::TranslationWriter(string
const &File
, string
const &TransCompress
,
507 mode_t
const &Permissions
) : Output(NULL
),
510 if (File
.empty() == true)
513 Comp
= new MultiCompress(File
, TransCompress
, Permissions
);
514 Output
= Comp
->Input
;
517 // TranslationWriter::DoPackage - Process a single package /*{{{*/
518 // ---------------------------------------------------------------------
519 /* Create a Translation-Master file for this Packages file */
520 bool TranslationWriter::DoPackage(string
const &Pkg
, string
const &Desc
,
526 // Different archs can include different versions and therefore
527 // different descriptions - so we need to check for both name and md5.
528 string
const Record
= Pkg
+ ":" + MD5
;
530 if (Included
.find(Record
) != Included
.end())
533 fprintf(Output
, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
534 Pkg
.c_str(), MD5
.c_str(), Desc
.c_str());
536 Included
.insert(Record
);
540 // TranslationWriter::~TranslationWriter - Destructor /*{{{*/
541 // ---------------------------------------------------------------------
543 TranslationWriter::~TranslationWriter()
552 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
553 // ---------------------------------------------------------------------
555 SourcesWriter::SourcesWriter(string
const &BOverrides
,string
const &SOverrides
,
556 string
const &ExtOverrides
)
564 // Process the command line options
565 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
567 // Read the override file
568 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
573 // WTF?? The logic above: if we can't read binary overrides, don't even try
574 // reading source overrides. if we can read binary overrides, then say there
575 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
577 if (ExtOverrides
.empty() == false)
578 SOver
.ReadExtraOverride(ExtOverrides
);
580 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
581 SOver
.ReadOverride(SOverrides
,true);
584 // SourcesWriter::DoPackage - Process a single package /*{{{*/
585 // ---------------------------------------------------------------------
587 bool SourcesWriter::DoPackage(string FileName
)
590 FileFd
F(FileName
,FileFd::ReadOnly
);
591 if (_error
->PendingError() == true)
594 // Stat the file for later
596 if (fstat(F
.Fd(),&St
) != 0)
597 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
599 if (St
.st_size
> 128*1024)
600 return _error
->Error("DSC file '%s' is too large!",FileName
.c_str());
602 if (BufSize
< (unsigned)St
.st_size
+1)
604 BufSize
= St
.st_size
+1;
605 Buffer
= (char *)realloc(Buffer
,St
.st_size
+1);
608 if (F
.Read(Buffer
,St
.st_size
) == false)
612 char *Start
= Buffer
;
613 char *BlkEnd
= Buffer
+ St
.st_size
;
615 MD5
.Add((unsigned char *)Start
,BlkEnd
- Start
);
618 SHA256Summation SHA256
;
619 SHA512Summation SHA512
;
620 SHA1
.Add((unsigned char *)Start
,BlkEnd
- Start
);
621 SHA256
.Add((unsigned char *)Start
,BlkEnd
- Start
);
622 SHA512
.Add((unsigned char *)Start
,BlkEnd
- Start
);
624 // Add an extra \n to the end, just in case
627 /* Remove the PGP trailer. Some .dsc's have this without a blank line
629 const char *Key
= "-----BEGIN PGP SIGNATURE-----";
630 for (char *MsgEnd
= Start
; MsgEnd
< BlkEnd
- strlen(Key
) -1; MsgEnd
++)
632 if (*MsgEnd
== '\n' && strncmp(MsgEnd
+1,Key
,strlen(Key
)) == 0)
639 /* Read records until we locate the Source record. This neatly skips the
640 GPG header (which is RFC822 formed) without any trouble. */
645 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
646 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
647 if (Tags
.Find("Source",Pos
) == true)
649 Start
+= Tags
.size();
654 // Lookup the overide information, finding first the best priority.
656 string Bins
= Tags
.FindS("Binary");
657 char Buffer
[Bins
.length() + 1];
658 auto_ptr
<Override::Item
> OverItem(0);
659 if (Bins
.empty() == false)
661 strcpy(Buffer
,Bins
.c_str());
663 // Ignore too-long errors.
665 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
667 // Look at all the binaries
668 unsigned char BestPrioV
= pkgCache::State::Extra
;
669 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
671 auto_ptr
<Override::Item
> Itm(BOver
.GetItem(BinList
[I
]));
675 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
676 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
678 BestPrioV
= NewPrioV
;
679 BestPrio
= Itm
->Priority
;
682 if (OverItem
.get() == 0)
687 // If we need to do any rewriting of the header do it now..
688 if (OverItem
.get() == 0)
690 if (NoOverride
== false)
693 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
696 OverItem
= auto_ptr
<Override::Item
>(new Override::Item
);
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::ostringstream ostreamFiles
;
716 if (Tags
.Exists("Files"))
717 ostreamFiles
<< "\n " << string(MD5
.Result()) << " " << St
.st_size
<< " "
718 << strippedName
<< "\n " << Tags
.FindS("Files");
719 string
const Files
= ostreamFiles
.str();
721 std::ostringstream ostreamSha1
;
722 if (Tags
.Exists("Checksums-Sha1"))
723 ostreamSha1
<< "\n " << string(SHA1
.Result()) << " " << St
.st_size
<< " "
724 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha1");
725 string
const ChecksumsSha1
= ostreamSha1
.str();
727 std::ostringstream ostreamSha256
;
728 if (Tags
.Exists("Checksums-Sha256"))
729 ostreamSha256
<< "\n " << string(SHA256
.Result()) << " " << St
.st_size
<< " "
730 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha256");
731 string
const ChecksumsSha256
= ostreamSha256
.str();
733 std::ostringstream ostreamSha512
;
734 if (Tags
.Exists("Checksums-Sha512"))
735 ostreamSha512
<< "\n " << string(SHA512
.Result()) << " " << St
.st_size
<< " "
736 << strippedName
<< "\n " << Tags
.FindS("Checksums-Sha512");
737 string
const ChecksumsSha512
= ostreamSha512
.str();
739 // Strip the DirStrip prefix from the FileName and add the PathPrefix
741 if (DirStrip
.empty() == false &&
742 FileName
.length() > DirStrip
.length() &&
743 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
744 NewFileName
= string(OriginalPath
+ DirStrip
.length());
746 NewFileName
= OriginalPath
;
747 if (PathPrefix
.empty() == false)
748 NewFileName
= flCombine(PathPrefix
,NewFileName
);
750 string Directory
= flNotFile(OriginalPath
);
751 string Package
= Tags
.FindS("Source");
753 // Perform the delinking operation over all of the files
755 const char *C
= Files
.c_str();
756 char *RealPath
= NULL
;
757 for (;isspace(*C
); C
++);
760 // Parse each of the elements
761 if (ParseQuoteWord(C
,ParseJnk
) == false ||
762 ParseQuoteWord(C
,ParseJnk
) == false ||
763 ParseQuoteWord(C
,ParseJnk
) == false)
764 return _error
->Error("Error parsing file record");
767 string OriginalPath
= Directory
+ ParseJnk
;
768 if (readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
769 (RealPath
= realpath(OriginalPath
.c_str(),NULL
)) != 0)
771 string RP
= RealPath
;
773 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
.st_size
) == false)
778 Directory
= flNotFile(NewFileName
);
779 if (Directory
.length() > 2)
780 Directory
.erase(Directory
.end()-1);
782 // This lists all the changes to the fields we are going to make.
783 // (5 hardcoded + checksums + maintainer + end marker)
784 TFRewriteData Changes
[5+2+1+SOverItem
->FieldOverride
.size()+1];
786 unsigned int End
= 0;
787 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
788 SetTFRewriteData(Changes
[End
++],"Files",Files
.c_str());
789 SetTFRewriteData(Changes
[End
++],"Checksums-Sha1",ChecksumsSha1
.c_str());
790 SetTFRewriteData(Changes
[End
++],"Checksums-Sha256",ChecksumsSha256
.c_str());
791 SetTFRewriteData(Changes
[End
++],"Checksums-Sha512",ChecksumsSha512
.c_str());
792 if (Directory
!= "./")
793 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
794 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
795 SetTFRewriteData(Changes
[End
++],"Status",0);
797 // Rewrite the maintainer field if necessary
799 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
800 if (MaintFailed
== true)
802 if (NoOverride
== false)
805 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
806 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
809 if (NewMaint
.empty() == false)
810 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
812 for (map
<string
,string
>::const_iterator I
= SOverItem
->FieldOverride
.begin();
813 I
!= SOverItem
->FieldOverride
.end(); I
++)
814 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
816 SetTFRewriteData(Changes
[End
++], 0, 0);
818 // Rewrite and store the fields.
819 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
821 fprintf(Output
,"\n");
829 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
830 // ---------------------------------------------------------------------
832 ContentsWriter::ContentsWriter(string
const &DB
, string
const &Arch
) :
833 FTWScanner(Arch
), Db(DB
), Stats(Db
.Stats
)
840 // ContentsWriter::DoPackage - Process a single package /*{{{*/
841 // ---------------------------------------------------------------------
842 /* If Package is the empty string the control record will be parsed to
843 determine what the package name is. */
844 bool ContentsWriter::DoPackage(string FileName
, string Package
)
846 if (!Db
.GetFileInfo(FileName
, Package
.empty(), true, false, false, false, false, false))
851 // Parse the package name
852 if (Package
.empty() == true)
854 Package
= Db
.Control
.Section
.FindS("Package");
857 Db
.Contents
.Add(Gen
,Package
);
862 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
863 // ---------------------------------------------------------------------
865 bool ContentsWriter::ReadFromPkgs(string
const &PkgFile
,string
const &PkgCompress
)
867 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
868 if (_error
->PendingError() == true)
871 // Open the package file
874 if (Pkgs
.OpenOld(CompFd
,Proc
) == false)
878 FileFd
Fd(CompFd
,false);
879 pkgTagFile
Tags(&Fd
);
880 if (_error
->PendingError() == true)
882 Pkgs
.CloseOld(CompFd
,Proc
);
887 pkgTagSection Section
;
888 while (Tags
.Step(Section
) == true)
890 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
891 string Package
= Section
.FindS("Section");
892 if (Package
.empty() == false && Package
.end()[-1] != '/')
895 Package
+= Section
.FindS("Package");
898 Package
+= Section
.FindS("Package");
900 DoPackage(File
,Package
);
901 if (_error
->empty() == false)
903 _error
->Error("Errors apply to file '%s'",File
.c_str());
904 _error
->DumpErrors();
908 // Tidy the compressor
909 if (Pkgs
.CloseOld(CompFd
,Proc
) == false)
917 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
918 // ---------------------------------------------------------------------
920 ReleaseWriter::ReleaseWriter(string
const &DB
)
922 if (_config
->FindB("APT::FTPArchive::Release::Default-Patterns", true) == true)
924 AddPattern("Packages");
925 AddPattern("Packages.gz");
926 AddPattern("Packages.bz2");
927 AddPattern("Packages.lzma");
928 AddPattern("Sources");
929 AddPattern("Sources.gz");
930 AddPattern("Sources.bz2");
931 AddPattern("Sources.lzma");
932 AddPattern("Release");
934 AddPattern("md5sum.txt");
936 AddPatterns(_config
->FindVector("APT::FTPArchive::Release::Patterns"));
939 time_t const now
= time(NULL
);
941 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
947 time_t const validuntil
= now
+ _config
->FindI("APT::FTPArchive::Release::ValidTime", 0);
949 if (now
== validuntil
||
950 strftime(validstr
, sizeof(validstr
), "%a, %d %b %Y %H:%M:%S UTC",
951 gmtime(&validuntil
)) == 0)
956 map
<string
,string
> Fields
;
957 Fields
["Origin"] = "";
958 Fields
["Label"] = "";
959 Fields
["Suite"] = "";
960 Fields
["Version"] = "";
961 Fields
["Codename"] = "";
962 Fields
["Date"] = datestr
;
963 Fields
["Valid-Until"] = validstr
;
964 Fields
["Architectures"] = "";
965 Fields
["Components"] = "";
966 Fields
["Description"] = "";
968 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
972 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
973 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
977 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
981 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
982 // ---------------------------------------------------------------------
983 bool ReleaseWriter::DoPackage(string FileName
)
985 // Strip the DirStrip prefix from the FileName and add the PathPrefix
987 if (DirStrip
.empty() == false &&
988 FileName
.length() > DirStrip
.length() &&
989 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
990 DirStrip
.begin(),DirStrip
.end()) == 0)
992 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
993 while (NewFileName
[0] == '/')
994 NewFileName
= string(NewFileName
.begin() + 1,NewFileName
.end());
997 NewFileName
= FileName
;
999 if (PathPrefix
.empty() == false)
1000 NewFileName
= flCombine(PathPrefix
,NewFileName
);
1002 FileFd
fd(FileName
, FileFd::ReadOnly
);
1009 CheckSums
[NewFileName
].size
= fd
.Size();
1012 MD5
.AddFD(fd
.Fd(), fd
.Size());
1013 CheckSums
[NewFileName
].MD5
= MD5
.Result();
1017 SHA1
.AddFD(fd
.Fd(), fd
.Size());
1018 CheckSums
[NewFileName
].SHA1
= SHA1
.Result();
1021 SHA256Summation SHA256
;
1022 SHA256
.AddFD(fd
.Fd(), fd
.Size());
1023 CheckSums
[NewFileName
].SHA256
= SHA256
.Result();
1025 SHA256Summation SHA512
;
1026 SHA256
.AddFD(fd
.Fd(), fd
.Size());
1027 CheckSums
[NewFileName
].SHA512
= SHA512
.Result();
1035 // ReleaseWriter::Finish - Output the checksums /*{{{*/
1036 // ---------------------------------------------------------------------
1037 void ReleaseWriter::Finish()
1039 fprintf(Output
, "MD5Sum:\n");
1040 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1041 I
!= CheckSums
.end();
1044 fprintf(Output
, " %s %16ld %s\n",
1045 (*I
).second
.MD5
.c_str(),
1047 (*I
).first
.c_str());
1050 fprintf(Output
, "SHA1:\n");
1051 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1052 I
!= CheckSums
.end();
1055 fprintf(Output
, " %s %16ld %s\n",
1056 (*I
).second
.SHA1
.c_str(),
1058 (*I
).first
.c_str());
1061 fprintf(Output
, "SHA256:\n");
1062 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1063 I
!= CheckSums
.end();
1066 fprintf(Output
, " %s %16ld %s\n",
1067 (*I
).second
.SHA256
.c_str(),
1069 (*I
).first
.c_str());
1072 fprintf(Output
, "SHA512:\n");
1073 for(map
<string
,struct CheckSum
>::const_iterator I
= CheckSums
.begin();
1074 I
!= CheckSums
.end();
1077 fprintf(Output
, " %s %32ld %s\n",
1078 (*I
).second
.SHA512
.c_str(),
1080 (*I
).first
.c_str());