1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.9 2003/12/26 20:50:01 mdz Exp $
4 /* ######################################################################
8 The file writer classes. These write various types of output, sources,
11 ##################################################################### */
13 // Include Files /*{{{*/
15 #pragma implementation "writer.h"
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/configuration.h>
24 #include <apt-pkg/md5.h>
25 #include <apt-pkg/sha1.h>
26 #include <apt-pkg/deblistparser.h>
28 #include <sys/types.h>
36 #include "apt-ftparchive.h"
37 #include "multicompress.h"
41 FTWScanner
*FTWScanner::Owner
;
43 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
44 // ---------------------------------------------------------------------
46 inline void SetTFRewriteData(struct TFRewriteData
&tfrd
,
49 const char *newtag
= 0)
52 tfrd
.Rewrite
= rewrite
;
57 // FTWScanner::FTWScanner - Constructor /*{{{*/
58 // ---------------------------------------------------------------------
60 FTWScanner::FTWScanner()
63 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
65 long PMax
= pathconf(".",_PC_PATH_MAX
);
67 RealPath
= new char[PMax
];
70 // FTWScanner::Scanner - FTW Scanner /*{{{*/
71 // ---------------------------------------------------------------------
72 /* This is the FTW scanner, it processes each directory element in the
74 int FTWScanner::Scanner(const char *File
,const struct stat
*sb
,int Flag
)
79 ioprintf(c1out
, _("W: Unable to read directory %s\n"), File
);
84 ioprintf(c1out
, _("W: Unable to stat %s\n"), File
);
89 const char *LastComponent
= strrchr(File
, '/');
90 if (LastComponent
== NULL
)
95 vector
<string
>::iterator I
;
96 for(I
= Owner
->Patterns
.begin(); I
!= Owner
->Patterns
.end(); ++I
)
98 if (fnmatch((*I
).c_str(), LastComponent
, 0) == 0)
101 if (I
== Owner
->Patterns
.end())
104 /* Process it. If the file is a link then resolve it into an absolute
105 name.. This works best if the directory components the scanner are
106 given are not links themselves. */
108 Owner
->OriginalPath
= File
;
109 if (Owner
->RealPath
!= 0 && readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
110 realpath(File
,Owner
->RealPath
) != 0)
111 Owner
->DoPackage(Owner
->RealPath
);
113 Owner
->DoPackage(File
);
115 if (_error
->empty() == false)
117 // Print any errors or warnings found
119 bool SeenPath
= false;
120 while (_error
->empty() == false)
124 bool Type
= _error
->PopMessage(Err
);
126 cerr
<< _("E: ") << Err
<< endl
;
128 cerr
<< _("W: ") << Err
<< endl
;
130 if (Err
.find(File
) != string::npos
)
134 if (SeenPath
== false)
135 cerr
<< _("E: Errors apply to file ") << "'" << File
<< "'" << endl
;
142 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
143 // ---------------------------------------------------------------------
145 bool FTWScanner::RecursiveScan(string Dir
)
147 /* If noprefix is set then jam the scan root in, so we don't generate
148 link followed paths out of control */
149 if (InternalPrefix
.empty() == true)
151 if (realpath(Dir
.c_str(),RealPath
) == 0)
152 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
153 InternalPrefix
= RealPath
;
156 // Do recursive directory searching
158 int Res
= ftw(Dir
.c_str(),Scanner
,30);
160 // Error treewalking?
163 if (_error
->PendingError() == false)
164 _error
->Errno("ftw",_("Tree walking failed"));
171 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
172 // ---------------------------------------------------------------------
173 /* This is an alternative to using FTW to locate files, it reads the list
174 of files from another file. */
175 bool FTWScanner::LoadFileList(string Dir
,string File
)
177 /* If noprefix is set then jam the scan root in, so we don't generate
178 link followed paths out of control */
179 if (InternalPrefix
.empty() == true)
181 if (realpath(Dir
.c_str(),RealPath
) == 0)
182 return _error
->Errno("realpath",_("Failed to resolve %s"),Dir
.c_str());
183 InternalPrefix
= RealPath
;
187 FILE *List
= fopen(File
.c_str(),"r");
189 return _error
->Errno("fopen",_("Failed to open %s"),File
.c_str());
191 /* We are a tad tricky here.. We prefix the buffer with the directory
192 name, that way if we need a full path with just use line.. Sneaky and
196 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
197 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
199 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
200 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
202 char *FileName
= _strstrip(FileStart
);
203 if (FileName
[0] == 0)
206 if (FileName
[0] != '/')
208 if (FileName
!= FileStart
)
209 memmove(FileStart
,FileName
,strlen(FileStart
));
215 if (stat(FileName
,&St
) != 0)
218 if (Scanner(FileName
,&St
,Flag
) != 0)
226 // FTWScanner::Delink - Delink symlinks /*{{{*/
227 // ---------------------------------------------------------------------
229 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
230 unsigned long &DeLinkBytes
,
233 // See if this isn't an internaly prefix'd file name.
234 if (InternalPrefix
.empty() == false &&
235 InternalPrefix
.length() < FileName
.length() &&
236 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
237 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
239 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
241 // Tidy up the display
242 if (DeLinkBytes
== 0)
246 ioprintf(c1out
, _(" DeLink %s [%s]\n"), (OriginalPath
+ InternalPrefix
.length()),
247 SizeToStr(St
.st_size
).c_str());
250 if (NoLinkAct
== false)
253 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
254 _error
->Errno("readlink",_("Failed to readlink %s"),OriginalPath
);
257 if (unlink(OriginalPath
) != 0)
258 _error
->Errno("unlink",_("Failed to unlink %s"),OriginalPath
);
261 if (link(FileName
.c_str(),OriginalPath
) != 0)
263 // Panic! Restore the symlink
264 symlink(OldLink
,OriginalPath
);
265 return _error
->Errno("link",_("*** Failed to link %s to %s"),
273 DeLinkBytes
+= St
.st_size
;
274 if (DeLinkBytes
/1024 >= DeLinkLimit
)
275 ioprintf(c1out
, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes
).c_str());
278 FileName
= OriginalPath
;
285 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
286 // ---------------------------------------------------------------------
288 PackagesWriter::PackagesWriter(string DB
,string Overrides
,string ExtOverrides
) :
289 Db(DB
),Stats(Db
.Stats
)
295 // Process the command line options
296 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
297 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
298 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
300 if (Db
.Loaded() == false)
303 // Read the override file
304 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
309 if (ExtOverrides
.empty() == false)
310 Over
.ReadExtraOverride(ExtOverrides
);
312 _error
->DumpErrors();
315 // FTWScanner::SetExts - Set extensions to support /*{{{*/
316 // ---------------------------------------------------------------------
318 bool FTWScanner::SetExts(string Vals
)
321 string::size_type Start
= 0;
322 for(string::size_type space
= Vals
.find(' ');
323 space
!= string::npos
;
324 space
= Vals
.find(' ', space
))
328 AddPattern(string("*") + string(Start
, space
-1));
337 // PackagesWriter::DoPackage - Process a single package /*{{{*/
338 // ---------------------------------------------------------------------
339 /* This method takes a package and gets its control information and
340 MD5 then writes out a control record with the proper fields rewritten
341 and the path/size/hash appended. */
342 bool PackagesWriter::DoPackage(string FileName
)
345 FileFd
F(FileName
,FileFd::ReadOnly
);
346 if (_error
->PendingError() == true)
349 // Stat the file for later
351 if (fstat(F
.Fd(),&St
) != 0)
352 return _error
->Errno("fstat",_("Failed to stat %s"),FileName
.c_str());
354 // Pull all the data we need form the DB
356 if (Db
.SetFile(FileName
,St
,&F
) == false ||
357 Db
.LoadControl() == false ||
358 (DoContents
== true && Db
.LoadContents(true) == false) ||
359 (DoMD5
== true && Db
.GetMD5(MD5Res
,false) == false))
362 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,St
) == false)
365 // Lookup the overide information
366 pkgTagSection
&Tags
= Db
.Control
.Section
;
367 string Package
= Tags
.FindS("Package");
369 Override::Item
*OverItem
= Over
.GetItem(Package
);
371 if (Package
.empty() == true)
372 return _error
->Error(_("Archive had no package field"));
374 // If we need to do any rewriting of the header do it now..
377 if (NoOverride
== false)
380 ioprintf(c1out
, _(" %s has no override entry\n"), Package
.c_str());
384 Tmp
.FieldOverride
["Section"] = Tags
.FindS("Section");
385 Tmp
.Priority
= Tags
.FindS("Priority");
389 sprintf(Size
,"%lu",St
.st_size
);
391 // Strip the DirStrip prefix from the FileName and add the PathPrefix
393 if (DirStrip
.empty() == false &&
394 FileName
.length() > DirStrip
.length() &&
395 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
396 DirStrip
.begin(),DirStrip
.end()) == 0)
397 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
399 NewFileName
= FileName
;
400 if (PathPrefix
.empty() == false)
401 NewFileName
= flCombine(PathPrefix
,NewFileName
);
403 // This lists all the changes to the fields we are going to make.
404 // (7 hardcoded + maintainer + suggests + end marker)
405 TFRewriteData Changes
[6+2+OverItem
->FieldOverride
.size()+1];
407 unsigned int End
= 0;
408 SetTFRewriteData(Changes
[End
++], "Size", Size
);
409 SetTFRewriteData(Changes
[End
++], "MD5sum", MD5Res
.c_str());
410 SetTFRewriteData(Changes
[End
++], "Filename", NewFileName
.c_str());
411 SetTFRewriteData(Changes
[End
++], "Priority", OverItem
->Priority
.c_str());
412 SetTFRewriteData(Changes
[End
++], "Status", 0);
413 SetTFRewriteData(Changes
[End
++], "Optional", 0);
415 // Rewrite the maintainer field if necessary
417 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
418 if (MaintFailed
== true)
420 if (NoOverride
== false)
423 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"),
424 Package
.c_str(), Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
428 if (NewMaint
.empty() == false)
429 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
431 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
432 dpkg-scanpackages does.. Well sort of. dpkg-scanpackages just does renaming
433 but dpkg does this append bit. So we do the append bit, at least that way the
434 status file and package file will remain similar. There are other transforms
435 but optional is the only legacy one still in use for some lazy reason. */
436 string OptionalStr
= Tags
.FindS("Optional");
437 if (OptionalStr
.empty() == false)
439 if (Tags
.FindS("Suggests").empty() == false)
440 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
441 SetTFRewriteData(Changes
[End
++], "Suggests", OptionalStr
.c_str());
444 for (map
<string
,string
>::iterator I
= OverItem
->FieldOverride
.begin();
445 I
!= OverItem
->FieldOverride
.end(); I
++)
446 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
448 SetTFRewriteData(Changes
[End
++], 0, 0);
450 // Rewrite and store the fields.
451 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
) == false)
453 fprintf(Output
,"\n");
459 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
460 // ---------------------------------------------------------------------
462 SourcesWriter::SourcesWriter(string BOverrides
,string SOverrides
,
471 // Process the command line options
472 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
474 // Read the override file
475 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
480 if (ExtOverrides
.empty() == false)
481 SOver
.ReadExtraOverride(ExtOverrides
);
483 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
484 SOver
.ReadOverride(SOverrides
,true);
487 // SourcesWriter::DoPackage - Process a single package /*{{{*/
488 // ---------------------------------------------------------------------
490 bool SourcesWriter::DoPackage(string FileName
)
493 FileFd
F(FileName
,FileFd::ReadOnly
);
494 if (_error
->PendingError() == true)
497 // Stat the file for later
499 if (fstat(F
.Fd(),&St
) != 0)
500 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
502 if (St
.st_size
> 128*1024)
503 return _error
->Error("DSC file '%s' is too large!",FileName
.c_str());
505 if (BufSize
< (unsigned)St
.st_size
+1)
507 BufSize
= St
.st_size
+1;
508 Buffer
= (char *)realloc(Buffer
,St
.st_size
+1);
511 if (F
.Read(Buffer
,St
.st_size
) == false)
515 char *Start
= Buffer
;
516 char *BlkEnd
= Buffer
+ St
.st_size
;
518 MD5
.Add((unsigned char *)Start
,BlkEnd
- Start
);
520 // Add an extra \n to the end, just in case
523 /* Remove the PGP trailer. Some .dsc's have this without a blank line
525 const char *Key
= "-----BEGIN PGP SIGNATURE-----";
526 for (char *MsgEnd
= Start
; MsgEnd
< BlkEnd
- strlen(Key
) -1; MsgEnd
++)
528 if (*MsgEnd
== '\n' && strncmp(MsgEnd
+1,Key
,strlen(Key
)) == 0)
535 /* Read records until we locate the Source record. This neatly skips the
536 GPG header (which is RFC822 formed) without any trouble. */
541 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
542 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
543 if (Tags
.Find("Source",Pos
) == true)
545 Start
+= Tags
.size();
550 // Lookup the overide information, finding first the best priority.
553 string Bins
= Tags
.FindS("Binary");
554 Override::Item
*OverItem
= 0;
555 if (Bins
.empty() == false && Bins
.length() < sizeof(Buffer
))
557 strcpy(Buffer
,Bins
.c_str());
559 // Ignore too-long errors.
561 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
563 // Look at all the binaries
564 unsigned char BestPrioV
= pkgCache::State::Extra
;
565 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
567 Override::Item
*Itm
= BOver
.GetItem(BinList
[I
]);
573 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
574 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
576 BestPrioV
= NewPrioV
;
577 BestPrio
= Itm
->Priority
;
582 // If we need to do any rewriting of the header do it now..
586 if (NoOverride
== false)
589 ioprintf(c1out
, _(" %s has no override entry\n"), Tags
.FindS("Source").c_str());
595 Override::Item
*SOverItem
= SOver
.GetItem(Tags
.FindS("Source"));
598 SOverItem
= BOver
.GetItem(Tags
.FindS("Source"));
600 SOverItem
= OverItem
;
603 // Add the dsc to the files hash list
605 snprintf(Files
,sizeof(Files
),"\n %s %lu %s\n %s",
606 string(MD5
.Result()).c_str(),St
.st_size
,
607 flNotDir(FileName
).c_str(),
608 Tags
.FindS("Files").c_str());
610 // Strip the DirStrip prefix from the FileName and add the PathPrefix
612 if (DirStrip
.empty() == false &&
613 FileName
.length() > DirStrip
.length() &&
614 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
615 NewFileName
= string(OriginalPath
+ DirStrip
.length());
617 NewFileName
= OriginalPath
;
618 if (PathPrefix
.empty() == false)
619 NewFileName
= flCombine(PathPrefix
,NewFileName
);
621 string Directory
= flNotFile(OriginalPath
);
622 string Package
= Tags
.FindS("Source");
624 // Perform the delinking operation over all of the files
626 const char *C
= Files
;
627 for (;isspace(*C
); C
++);
630 // Parse each of the elements
631 if (ParseQuoteWord(C
,ParseJnk
) == false ||
632 ParseQuoteWord(C
,ParseJnk
) == false ||
633 ParseQuoteWord(C
,ParseJnk
) == false)
634 return _error
->Error("Error parsing file record");
637 string OriginalPath
= Directory
+ ParseJnk
;
638 if (RealPath
!= 0 && readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
639 realpath(OriginalPath
.c_str(),RealPath
) != 0)
641 string RP
= RealPath
;
642 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
) == false)
647 Directory
= flNotFile(NewFileName
);
648 if (Directory
.length() > 2)
649 Directory
.erase(Directory
.end()-1);
651 // This lists all the changes to the fields we are going to make.
652 // (5 hardcoded + maintainer + end marker)
653 TFRewriteData Changes
[5+1+SOverItem
->FieldOverride
.size()+1];
655 unsigned int End
= 0;
656 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
657 SetTFRewriteData(Changes
[End
++],"Files",Files
);
658 if (Directory
!= "./")
659 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
660 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
661 SetTFRewriteData(Changes
[End
++],"Status",0);
663 // Rewrite the maintainer field if necessary
665 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
666 if (MaintFailed
== true)
668 if (NoOverride
== false)
671 ioprintf(c1out
, _(" %s maintainer is %s not %s\n"), Package
.c_str(),
672 Tags
.FindS("Maintainer").c_str(), OverItem
->OldMaint
.c_str());
675 if (NewMaint
.empty() == false)
676 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
678 for (map
<string
,string
>::iterator I
= SOverItem
->FieldOverride
.begin();
679 I
!= SOverItem
->FieldOverride
.end(); I
++)
680 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
682 SetTFRewriteData(Changes
[End
++], 0, 0);
684 // Rewrite and store the fields.
685 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
687 fprintf(Output
,"\n");
695 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
696 // ---------------------------------------------------------------------
698 ContentsWriter::ContentsWriter(string DB
) :
699 Db(DB
), Stats(Db
.Stats
)
706 // ContentsWriter::DoPackage - Process a single package /*{{{*/
707 // ---------------------------------------------------------------------
708 /* If Package is the empty string the control record will be parsed to
709 determine what the package name is. */
710 bool ContentsWriter::DoPackage(string FileName
,string Package
)
713 FileFd
F(FileName
,FileFd::ReadOnly
);
714 if (_error
->PendingError() == true)
717 // Stat the file for later
719 if (fstat(F
.Fd(),&St
) != 0)
720 return _error
->Errno("fstat","Failed too stat %s",FileName
.c_str());
723 if (Db
.SetFile(FileName
,St
,&F
) == false ||
724 Db
.LoadContents(false) == false)
727 // Parse the package name
728 if (Package
.empty() == true)
730 if (Db
.LoadControl() == false)
732 Package
= Db
.Control
.Section
.FindS("Package");
735 Db
.Contents
.Add(Gen
,Package
);
740 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
741 // ---------------------------------------------------------------------
743 bool ContentsWriter::ReadFromPkgs(string PkgFile
,string PkgCompress
)
745 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
746 if (_error
->PendingError() == true)
749 // Open the package file
752 if (Pkgs
.OpenOld(CompFd
,Proc
) == false)
756 FileFd
Fd(CompFd
,false);
757 pkgTagFile
Tags(&Fd
);
758 if (_error
->PendingError() == true)
760 Pkgs
.CloseOld(CompFd
,Proc
);
765 pkgTagSection Section
;
766 while (Tags
.Step(Section
) == true)
768 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
769 string Package
= Section
.FindS("Section");
770 if (Package
.empty() == false && Package
.end()[-1] != '/')
773 Package
+= Section
.FindS("Package");
776 Package
+= Section
.FindS("Package");
778 DoPackage(File
,Package
);
779 if (_error
->empty() == false)
781 _error
->Error("Errors apply to file '%s'",File
.c_str());
782 _error
->DumpErrors();
786 // Tidy the compressor
787 if (Pkgs
.CloseOld(CompFd
,Proc
) == false)
795 // ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
796 // ---------------------------------------------------------------------
798 ReleaseWriter::ReleaseWriter(string DB
)
800 AddPattern("Packages");
801 AddPattern("Packages.gz");
803 time_t now
= time(NULL
);
805 if (strftime(datestr
, sizeof(datestr
), "%a, %d %b %Y %H:%M:%S UTC",
811 map
<string
,string
> Fields
;
812 Fields
["Origin"] = "";
813 Fields
["Label"] = "";
814 Fields
["Suite"] = "";
815 Fields
["Version"] = "";
816 Fields
["Codename"] = "";
817 Fields
["Date"] = datestr
;
818 Fields
["Architectures"] = "";
819 Fields
["Components"] = "";
820 Fields
["Description"] = "";
822 for(map
<string
,string
>::const_iterator I
= Fields
.begin();
826 string Config
= string("APT::FTPArchive::Release::") + (*I
).first
;
827 string Value
= _config
->Find(Config
, (*I
).second
.c_str());
831 fprintf(Output
, "%s: %s\n", (*I
).first
.c_str(), Value
.c_str());
835 // ReleaseWriter::DoPackage - Process a single package /*{{{*/
836 // ---------------------------------------------------------------------
837 bool ReleaseWriter::DoPackage(string FileName
)
839 // Strip the DirStrip prefix from the FileName and add the PathPrefix
841 if (DirStrip
.empty() == false &&
842 FileName
.length() > DirStrip
.length() &&
843 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
844 DirStrip
.begin(),DirStrip
.end()) == 0)
845 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
847 NewFileName
= FileName
;
848 if (PathPrefix
.empty() == false)
849 NewFileName
= flCombine(PathPrefix
,NewFileName
);
851 FileFd
fd(FileName
, FileFd::ReadOnly
);
858 CheckSums
[FileName
].size
= fd
.Size();
861 MD5
.AddFD(fd
.Fd(), fd
.Size());
862 CheckSums
[FileName
].MD5
= MD5
.Result();
866 SHA1
.AddFD(fd
.Fd(), fd
.Size());
867 CheckSums
[FileName
].SHA1
= SHA1
.Result();
875 // ReleaseWriter::Finish - Output the checksums /*{{{*/
876 // ---------------------------------------------------------------------
877 void ReleaseWriter::Finish()
879 fprintf(Output
, "MD5Sum:\n");
880 for(map
<string
,struct CheckSum
>::iterator I
= CheckSums
.begin();
881 I
!= CheckSums
.end();
884 fprintf(Output
, " %s %16ld %s\n",
885 (*I
).second
.MD5
.c_str(),
890 fprintf(Output
, "SHA1:\n");
891 for(map
<string
,struct CheckSum
>::iterator I
= CheckSums
.begin();
892 I
!= CheckSums
.end();
895 fprintf(Output
, " %s %16ld %s\n",
896 (*I
).second
.SHA1
.c_str(),