]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-ftparchive.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $
4 /* ######################################################################
6 apt-scanpackages - Efficient work-alike for dpkg-scanpackages
8 Let contents be disabled from the conf
10 ##################################################################### */
12 // Include Files /*{{{*/
14 #pragma implementation "apt-ftparchive.h"
17 #include "apt-ftparchive.h"
19 #include <apt-pkg/error.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/cmndline.h>
22 #include <apt-pkg/strutl.h>
31 #include "multicompress.h"
38 ofstream
devnull("/dev/null");
41 // struct PackageMap - List of all package files in the config file /*{{{*/
42 // ---------------------------------------------------------------------
48 string InternalPrefix
;
53 // Stuff for the Package File
58 // Stuff for the Source File
72 unsigned int DeLinkLimit
;
80 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
82 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
83 {return x
.ContentsMTime
< y
.ContentsMTime
;};
86 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
88 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
89 {return x
.BinCacheDB
< y
.BinCacheDB
;};
92 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
93 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
94 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
95 bool GenContents(Configuration
&Setup
,
96 PackageMap
*Begin
,PackageMap
*End
,
99 PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
100 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
104 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
105 // ---------------------------------------------------------------------
107 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
109 PathPrefix
= Block
.Find("PathPrefix");
111 if (Block
.FindB("External-Links",true) == false)
112 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
116 PkgCompress
= Block
.Find("Packages::Compress",
117 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
118 CntCompress
= Block
.Find("Contents::Compress",
119 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
120 SrcCompress
= Block
.Find("Sources::Compress",
121 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
123 SrcExt
= Block
.Find("Sources::Extensions",
124 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
125 PkgExt
= Block
.Find("Packages::Extensions",
126 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
128 Permissions
= Setup
.FindI("Default::FileMode",0644);
130 if (FLFile
.empty() == false)
131 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
137 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
138 // ---------------------------------------------------------------------
139 /* This generates the Package File described by this object. */
140 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
142 if (PkgFile
.empty() == true)
145 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
146 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
147 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
149 struct timeval StartTime
;
150 gettimeofday(&StartTime
,0);
154 // Create a package writer object.
155 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
156 flCombine(OverrideDir
,BinOverride
));
157 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
158 return _error
->Error("Package extension list is too long");
159 if (_error
->PendingError() == true)
160 return _error
->Error("Error Processing directory %s",BaseDir
.c_str());
162 Packages
.PathPrefix
= PathPrefix
;
163 Packages
.DirStrip
= ArchiveDir
;
164 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
166 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
167 Packages
.DeLinkLimit
= DeLinkLimit
;
169 // Create a compressor object
170 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
171 PkgCompress
,Permissions
);
172 Packages
.Output
= Comp
.Input
;
173 if (_error
->PendingError() == true)
174 return _error
->Error("Error Processing directory %s",BaseDir
.c_str());
176 c0out
<< ' ' << BaseDir
<< ":" << flush
;
178 // Do recursive directory searching
179 if (FLFile
.empty() == true)
181 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
186 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
190 Packages
.Output
= 0; // Just in case
192 // Finish compressing
194 if (Comp
.Finalize(Size
) == false)
197 return _error
->Error("Error Processing directory %s",BaseDir
.c_str());
202 << SizeToStr(Size
) << "B ";
206 struct timeval NewTime
;
207 gettimeofday(&NewTime
,0);
208 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
209 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
211 c0out
<< Packages
.Stats
.Packages
<< " files " <<
212 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
213 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
214 TimeToStr((long)Delta
) << endl
;
216 Stats
.Add(Packages
.Stats
);
217 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
219 return !_error
->PendingError();
222 // PackageMap::GenSources - Actually generate a Package file /*{{{*/
223 // ---------------------------------------------------------------------
224 /* This generates the Sources File described by this object. */
225 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
227 if (SrcFile
.empty() == true)
230 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
231 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
232 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
234 struct timeval StartTime
;
235 gettimeofday(&StartTime
,0);
239 // Create a package writer object.
240 SourcesWriter
Sources(flCombine(OverrideDir
,BinOverride
),
241 flCombine(OverrideDir
,SrcOverride
));
242 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
243 return _error
->Error("Source extension list is too long");
244 if (_error
->PendingError() == true)
245 return _error
->Error("Error Processing directory %s",BaseDir
.c_str());
247 Sources
.PathPrefix
= PathPrefix
;
248 Sources
.DirStrip
= ArchiveDir
;
249 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
251 Sources
.DeLinkLimit
= DeLinkLimit
;
252 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
254 // Create a compressor object
255 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
256 SrcCompress
,Permissions
);
257 Sources
.Output
= Comp
.Input
;
258 if (_error
->PendingError() == true)
259 return _error
->Error("Error Processing directory %s",BaseDir
.c_str());
261 c0out
<< ' ' << BaseDir
<< ":" << flush
;
263 // Do recursive directory searching
264 if (FLFile
.empty() == true)
266 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
271 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
274 Sources
.Output
= 0; // Just in case
276 // Finish compressing
278 if (Comp
.Finalize(Size
) == false)
281 return _error
->Error("Error Processing directory %s",BaseDir
.c_str());
286 << SizeToStr(Size
) << "B ";
290 struct timeval NewTime
;
291 gettimeofday(&NewTime
,0);
292 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
293 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
295 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
296 TimeToStr((long)Delta
) << endl
;
298 Stats
.Add(Sources
.Stats
);
299 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
301 return !_error
->PendingError();
304 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
305 // ---------------------------------------------------------------------
306 /* This generates the contents file partially described by this object.
307 It searches the given iterator range for other package files that map
308 into this contents file and includes their data as well when building. */
309 bool PackageMap::GenContents(Configuration
&Setup
,
310 PackageMap
*Begin
,PackageMap
*End
,
313 if (Contents
.empty() == true)
319 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
320 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
321 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
323 struct timeval StartTime
;
324 gettimeofday(&StartTime
,0);
326 // Create a package writer object.
327 ContentsWriter
Contents("");
328 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
329 return _error
->Error("Package extension list is too long");
330 if (_error
->PendingError() == true)
333 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
334 CntCompress
,Permissions
);
335 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
336 Contents
.Output
= Comp
.Input
;
337 if (_error
->PendingError() == true)
340 // Write the header out.
341 if (ContentsHead
.empty() == false)
343 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
344 if (_error
->PendingError() == true)
347 unsigned long Size
= Head
.Size();
348 unsigned char Buf
[4096];
351 unsigned long ToRead
= Size
;
352 if (Size
> sizeof(Buf
))
353 ToRead
= sizeof(Buf
);
355 if (Head
.Read(Buf
,ToRead
) == false)
358 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
359 return _error
->Errno("fwrite","Error writing header to contents file");
365 /* Go over all the package file records and parse all the package
366 files associated with this contents file into one great big honking
367 memory structure, then dump the sorted version */
368 c0out
<< ' ' << this->Contents
<< ":" << flush
;
369 for (PackageMap
*I
= Begin
; I
!= End
; I
++)
371 if (I
->Contents
!= this->Contents
)
374 Contents
.Prefix
= ArchiveDir
;
375 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
376 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
379 I
->ContentsDone
= true;
384 // Finish compressing
386 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
389 return _error
->Error("Error Processing Contents %s",
390 this->Contents
.c_str());
395 c0out
<< " New " << SizeToStr(Size
) << "B ";
404 struct timeval NewTime
;
405 gettimeofday(&NewTime
,0);
406 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
407 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
409 c0out
<< Contents
.Stats
.Packages
<< " files " <<
410 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
411 TimeToStr((long)Delta
) << endl
;
417 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
418 // ---------------------------------------------------------------------
419 /* This populates the PkgList with all the possible permutations of the
420 section/arch lists. */
421 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
424 string DDir
= Setup
.Find("TreeDefault::Directory",
425 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
426 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
427 "$(DIST)/$(SECTION)/source/");
428 string DPkg
= Setup
.Find("TreeDefault::Packages",
429 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
430 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
431 "$(DIST)/$(SECTION)/");
432 string DContents
= Setup
.Find("TreeDefault::Contents",
433 "$(DIST)/Contents-$(ARCH)");
434 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
435 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
436 "packages-$(ARCH).db");
437 string DSources
= Setup
.Find("TreeDefault::Sources",
438 "$(DIST)/$(SECTION)/source/Sources");
439 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
440 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
442 // Process 'tree' type sections
443 const Configuration::Item
*Top
= Setup
.Tree("tree");
444 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
446 Configuration
Block(Top
);
447 string Dist
= Top
->Tag
;
449 // Parse the sections
450 const char *Sections
= Block
.Find("Sections").c_str();
452 while (ParseQuoteWord(Sections
,Section
) == true)
454 const char *Archs
= Block
.Find("Architectures").c_str();
456 while (ParseQuoteWord(Archs
,Arch
) == true)
458 struct SubstVar Vars
[] = {{"$(DIST)",&Dist
},
459 {"$(SECTION)",&Section
},
464 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
465 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
467 if (stringcasecmp(Arch
,"source") == 0)
469 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
470 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
471 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
472 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
473 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
477 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
478 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
479 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
480 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
481 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
482 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
483 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
486 Itm
.GetGeneral(Setup
,Block
);
487 PkgList
.push_back(Itm
);
495 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
496 // ---------------------------------------------------------------------
498 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
500 // Process 'bindirectory' type sections
501 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
502 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
504 Configuration
Block(Top
);
507 Itm
.PkgFile
= Block
.Find("Packages");
508 Itm
.SrcFile
= Block
.Find("Sources");
509 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
510 Itm
.BinOverride
= Block
.Find("BinOverride");
511 Itm
.SrcOverride
= Block
.Find("SrcOverride");
512 Itm
.BaseDir
= Top
->Tag
;
513 Itm
.FLFile
= Block
.Find("FileList");
514 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
515 Itm
.Contents
= Block
.Find("Contents");
516 Itm
.ContentsHead
= Block
.Find("Contents::Header");
518 Itm
.GetGeneral(Setup
,Block
);
519 PkgList
.push_back(Itm
);
526 // ShowHelp - Show the help text /*{{{*/
527 // ---------------------------------------------------------------------
529 bool ShowHelp(CommandLine
&CmdL
)
531 ioprintf(cout
,_("%s %s for %s %s compiled on %s %s\n"),PACKAGE
,VERSION
,
532 COMMON_OS
,COMMON_CPU
,__DATE__
,__TIME__
);
533 if (_config
->FindB("version") == true)
537 "Usage: apt-ftparchive [options] command\n"
538 "Commands: packges binarypath [overridefile [pathprefix]]\n"
539 " sources srcpath [overridefile [pathprefix]]\n"
541 " generate config [groups]\n"
544 "apt-ftparchive generates index files for Debian archives. It supports\n"
545 "many styles of generation from fully automated to functional replacements\n"
546 "for dpkg-scanpackages and dpkg-scansources\n"
548 "apt-ftparchive generates Package files from a tree of .debs. The\n"
549 "Package file contains the contents of all the control fields from\n"
550 "each package as well as the MD5 hash and filesize. An override file\n"
551 "is supported to force the value of Priority and Section.\n"
553 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
554 "The --source-override option can be used to specify a src override file\n"
556 "The 'packages' and 'sources' command should be run in the root of the\n"
557 "tree. BinaryPath should point to the base of the recursive search and \n"
558 "override file should contian the override flags. Pathprefix is\n"
559 "appended to the filename fields if present. Example usage from the \n"
561 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
562 " dists/potato/main/binary-i386/Packages\n"
565 " -h This help text\n"
566 " --md5 Control MD5 generation\n"
567 " -s=? Source override file\n"
569 " -d=? Select the optional caching database\n"
570 " --no-delink Enable delinking debug mode\n"
571 " --contents Control contents file generation\n"
572 " -c=? Read this configuration file\n"
573 " -o=? Set an arbitary configuration option" << endl
;
578 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
579 // ---------------------------------------------------------------------
580 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
581 bool SimpleGenPackages(CommandLine
&CmdL
)
583 if (CmdL
.FileSize() < 2)
584 return ShowHelp(CmdL
);
587 if (CmdL
.FileSize() >= 3)
588 Override
= CmdL
.FileList
[2];
590 // Create a package writer object.
591 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
593 if (_error
->PendingError() == true)
596 if (CmdL
.FileSize() >= 4)
597 Packages
.PathPrefix
= CmdL
.FileList
[3];
599 // Do recursive directory searching
600 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
606 // SimpleGenContents - Generate a Contents listing /*{{{*/
607 // ---------------------------------------------------------------------
609 bool SimpleGenContents(CommandLine
&CmdL
)
611 if (CmdL
.FileSize() < 2)
612 return ShowHelp(CmdL
);
614 // Create a package writer object.
615 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"));
616 if (_error
->PendingError() == true)
619 // Do recursive directory searching
620 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
628 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
629 // ---------------------------------------------------------------------
630 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
631 bool SimpleGenSources(CommandLine
&CmdL
)
633 if (CmdL
.FileSize() < 2)
634 return ShowHelp(CmdL
);
637 if (CmdL
.FileSize() >= 3)
638 Override
= CmdL
.FileList
[2];
641 if (Override
.empty() == false)
642 SOverride
= Override
+ ".src";
644 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
647 // Create a package writer object.
648 SourcesWriter
Sources(Override
,SOverride
);
649 if (_error
->PendingError() == true)
652 if (CmdL
.FileSize() >= 4)
653 Sources
.PathPrefix
= CmdL
.FileList
[3];
655 // Do recursive directory searching
656 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
662 // Generate - Full generate, using a config file /*{{{*/
663 // ---------------------------------------------------------------------
665 bool Generate(CommandLine
&CmdL
)
667 struct CacheDB::Stats SrcStats
;
668 if (CmdL
.FileSize() < 2)
669 return ShowHelp(CmdL
);
671 struct timeval StartTime
;
672 gettimeofday(&StartTime
,0);
673 struct CacheDB::Stats Stats
;
675 // Read the configuration file.
677 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
680 vector
<PackageMap
> PkgList
;
681 LoadTree(PkgList
,Setup
);
682 LoadBinDir(PkgList
,Setup
);
684 // Sort by cache DB to improve IO locality.
685 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
688 if (CmdL
.FileSize() <= 2)
690 for (PackageMap
*I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
691 if (I
->GenPackages(Setup
,Stats
) == false)
692 _error
->DumpErrors();
693 for (PackageMap
*I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
694 if (I
->GenSources(Setup
,SrcStats
) == false)
695 _error
->DumpErrors();
699 // Make a choice list out of the package list..
700 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
701 RxChoiceList
*End
= List
;
702 for (PackageMap
*I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
705 End
->Str
= I
->BaseDir
.c_str();
709 End
->Str
= I
->Tag
.c_str();
715 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
718 return _error
->Error("No selections matched");
720 _error
->DumpErrors();
722 // Do the generation for Packages
723 for (End
= List
; End
->Str
!= 0; End
++)
725 if (End
->Hit
== false)
728 PackageMap
*I
= (PackageMap
*)End
->UserData
;
729 if (I
->PkgDone
== true)
731 if (I
->GenPackages(Setup
,Stats
) == false)
732 _error
->DumpErrors();
735 // Do the generation for Sources
736 for (End
= List
; End
->Str
!= 0; End
++)
738 if (End
->Hit
== false)
741 PackageMap
*I
= (PackageMap
*)End
->UserData
;
742 if (I
->SrcDone
== true)
744 if (I
->GenSources(Setup
,SrcStats
) == false)
745 _error
->DumpErrors();
751 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
754 c1out
<< "Done Packages, Starting contents." << endl
;
756 // Sort the contents file list by date
757 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
758 for (PackageMap
*I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
761 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
762 I
->CntCompress
,A
) == false)
763 time(&I
->ContentsMTime
);
765 I
->ContentsMTime
= A
.st_mtime
;
767 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
769 /* Now for Contents.. The process here is to do a make-like dependency
770 check. Each contents file is verified to be newer than the package files
771 that describe the debs it indexes. Since the package files contain
772 hashes of the .debs this means they have not changed either so the
773 contents must be up to date. */
774 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
775 for (PackageMap
*I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
777 // This record is not relevent
778 if (I
->ContentsDone
== true ||
779 I
->Contents
.empty() == true)
782 // Do not do everything if the user specified sections.
783 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
787 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
789 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
791 _error
->Warning("Some files are missing in the package file group `%s'",I
->PkgFile
.c_str());
795 if (A
.st_mtime
> B
.st_mtime
)
799 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
800 MaxContentsChange
) == false)
801 _error
->DumpErrors();
804 if (MaxContentsChange
== 0)
806 c1out
<< "Hit contents update byte limit" << endl
;
811 struct timeval NewTime
;
812 gettimeofday(&NewTime
,0);
813 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
814 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
815 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
816 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
821 // Clean - Clean out the databases /*{{{*/
822 // ---------------------------------------------------------------------
824 bool Clean(CommandLine
&CmdL
)
826 if (CmdL
.FileSize() != 2)
827 return ShowHelp(CmdL
);
829 // Read the configuration file.
831 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
834 vector
<PackageMap
> PkgList
;
835 LoadTree(PkgList
,Setup
);
836 LoadBinDir(PkgList
,Setup
);
838 // Sort by cache DB to improve IO locality.
839 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
841 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
843 for (PackageMap
*I
= PkgList
.begin(); I
!= PkgList
.end(); )
845 c0out
<< I
->BinCacheDB
<< endl
;
846 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
847 if (DB
.Clean() == false)
848 _error
->DumpErrors();
850 string CacheDB
= I
->BinCacheDB
;
851 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; I
++);
858 int main(int argc
, const char *argv
[])
860 CommandLine::Args Args
[] = {
861 {'h',"help","help",0},
862 {0,"md5","APT::FTPArchive::MD5",0},
863 {'v',"version","version",0},
864 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
865 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
866 {'q',"quiet","quiet",CommandLine::IntLevel
},
867 {'q',"silent","quiet",CommandLine::IntLevel
},
868 {0,"delink","APT::FTPArchive::DeLinkAct",0},
869 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
870 {0,"contents","APT::FTPArchive::Contents",0},
871 {'c',"config-file",0,CommandLine::ConfigFile
},
872 {'o',"option",0,CommandLine::ArbItem
},
874 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
875 {"contents",&SimpleGenContents
},
876 {"sources",&SimpleGenSources
},
877 {"generate",&Generate
},
882 // Parse the command line and initialize the package library
883 CommandLine
CmdL(Args
,_config
);
884 if (CmdL
.Parse(argc
,argv
) == false)
886 _error
->DumpErrors();
890 // See if the help should be shown
891 if (_config
->FindB("help") == true ||
892 _config
->FindB("version") == true ||
893 CmdL
.FileSize() == 0)
899 // Setup the output streams
900 c0out
.rdbuf(cout
.rdbuf());
901 c1out
.rdbuf(cout
.rdbuf());
902 c2out
.rdbuf(cout
.rdbuf());
903 Quiet
= _config
->FindI("quiet",0);
905 c0out
.rdbuf(devnull
.rdbuf());
907 c1out
.rdbuf(devnull
.rdbuf());
909 // Match the operation
910 CmdL
.DispatchArg(Cmds
);
912 if (_error
->empty() == false)
914 bool Errors
= _error
->PendingError();
915 _error
->DumpErrors();
916 return Errors
== true?100:0;