2 #include <mach-o/nlist.h>
5 // -*- mode: cpp; mode: fold -*-
7 // $Id: apt-ftparchive.cc,v 1.8.2.3 2004/01/02 22:01:48 mdz Exp $
8 /* ######################################################################
10 apt-scanpackages - Efficient work-alike for dpkg-scanpackages
12 Let contents be disabled from the conf
14 ##################################################################### */
16 // Include Files /*{{{*/
18 #pragma implementation "apt-ftparchive.h"
21 #include "apt-ftparchive.h"
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/configuration.h>
25 #include <apt-pkg/cmndline.h>
26 #include <apt-pkg/strutl.h>
35 #include "multicompress.h"
43 ofstream devnull("/dev/null");
46 // struct PackageMap - List of all package files in the config file /*{{{*/
47 // ---------------------------------------------------------------------
53 string InternalPrefix;
58 // Stuff for the Package File
64 // We generate for this given arch
67 // Stuff for the Source File
70 string SrcExtraOverride;
82 unsigned int DeLinkLimit;
90 struct ContentsCompare : public binary_function<PackageMap,PackageMap,bool>
92 inline bool operator() (const PackageMap &x,const PackageMap &y)
93 {return x.ContentsMTime < y.ContentsMTime;};
96 struct DBCompare : public binary_function<PackageMap,PackageMap,bool>
98 inline bool operator() (const PackageMap &x,const PackageMap &y)
99 {return x.BinCacheDB < y.BinCacheDB;};
102 void GetGeneral(Configuration &Setup,Configuration &Block);
103 bool GenPackages(Configuration &Setup,struct CacheDB::Stats &Stats);
104 bool GenSources(Configuration &Setup,struct CacheDB::Stats &Stats);
105 bool GenContents(Configuration &Setup,
106 vector<PackageMap>::iterator Begin,
107 vector<PackageMap>::iterator End,
108 unsigned long &Left);
110 PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
111 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
115 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
116 // ---------------------------------------------------------------------
118 void PackageMap::GetGeneral(Configuration &Setup,Configuration &Block)
120 PathPrefix = Block.Find("PathPrefix");
122 if (Block.FindB("External-Links",true) == false)
123 DeLinkLimit = Setup.FindI("Default::DeLinkLimit",UINT_MAX);
127 PkgCompress = Block.Find("Packages::Compress",
128 Setup.Find("Default::Packages::Compress",". gzip").c_str());
129 CntCompress = Block.Find("Contents::Compress",
130 Setup.Find("Default::Contents::Compress",". gzip").c_str());
131 SrcCompress = Block.Find("Sources::Compress",
132 Setup.Find("Default::Sources::Compress",". gzip").c_str());
134 SrcExt = Block.Find("Sources::Extensions",
135 Setup.Find("Default::Sources::Extensions",".dsc").c_str());
136 PkgExt = Block.Find("Packages::Extensions",
137 Setup.Find("Default::Packages::Extensions",".deb").c_str());
139 Permissions = Setup.FindI("Default::FileMode",0644);
141 if (FLFile.empty() == false)
142 FLFile = flCombine(Setup.Find("Dir::FileListDir"),FLFile);
148 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
149 // ---------------------------------------------------------------------
150 /* This generates the Package File described by this object. */
151 bool PackageMap::GenPackages(Configuration &Setup,struct CacheDB::Stats &Stats)
153 if (PkgFile.empty() == true)
156 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
157 string OverrideDir = Setup.FindDir("Dir::OverrideDir");
158 string CacheDir = Setup.FindDir("Dir::CacheDir");
160 struct timeval StartTime;
161 gettimeofday(&StartTime,0);
165 // Create a package writer object.
166 PackagesWriter Packages(flCombine(CacheDir,BinCacheDB),
167 flCombine(OverrideDir,BinOverride),
168 flCombine(OverrideDir,ExtraOverride),
170 if (PkgExt.empty() == false && Packages.SetExts(PkgExt) == false)
171 return _error->Error(_("Package extension list is too long"));
172 if (_error->PendingError() == true)
173 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
175 Packages.PathPrefix = PathPrefix;
176 Packages.DirStrip = ArchiveDir;
177 Packages.InternalPrefix = flCombine(ArchiveDir,InternalPrefix);
179 Packages.Stats.DeLinkBytes = Stats.DeLinkBytes;
180 Packages.DeLinkLimit = DeLinkLimit;
182 // Create a compressor object
183 MultiCompress Comp(flCombine(ArchiveDir,PkgFile),
184 PkgCompress,Permissions);
185 Packages.Output = Comp.Input;
186 if (_error->PendingError() == true)
187 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
189 c0out << ' ' << BaseDir << ":" << flush;
191 // Do recursive directory searching
192 if (FLFile.empty() == true)
194 if (Packages.RecursiveScan(flCombine(ArchiveDir,BaseDir)) == false)
199 if (Packages.LoadFileList(ArchiveDir,FLFile) == false)
203 Packages.Output = 0; // Just in case
205 // Finish compressing
207 if (Comp.Finalize(Size) == false)
210 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
215 << SizeToStr(Size) << "B ";
219 struct timeval NewTime;
220 gettimeofday(&NewTime,0);
221 double Delta = NewTime.tv_sec - StartTime.tv_sec +
222 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
224 c0out << Packages.Stats.Packages << " files " <<
225 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
226 SizeToStr(Packages.Stats.Bytes) << "B " <<
227 TimeToStr((long)Delta) << endl;
229 Stats.Add(Packages.Stats);
230 Stats.DeLinkBytes = Packages.Stats.DeLinkBytes;
232 return !_error->PendingError();
236 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
237 // ---------------------------------------------------------------------
238 /* This generates the Sources File described by this object. */
239 bool PackageMap::GenSources(Configuration &Setup,struct CacheDB::Stats &Stats)
241 if (SrcFile.empty() == true)
244 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
245 string OverrideDir = Setup.FindDir("Dir::OverrideDir");
246 string CacheDir = Setup.FindDir("Dir::CacheDir");
248 struct timeval StartTime;
249 gettimeofday(&StartTime,0);
253 // Create a package writer object.
254 SourcesWriter Sources(flCombine(OverrideDir,BinOverride),
255 flCombine(OverrideDir,SrcOverride),
256 flCombine(OverrideDir,SrcExtraOverride));
257 if (SrcExt.empty() == false && Sources.SetExts(SrcExt) == false)
258 return _error->Error(_("Source extension list is too long"));
259 if (_error->PendingError() == true)
260 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
262 Sources.PathPrefix = PathPrefix;
263 Sources.DirStrip = ArchiveDir;
264 Sources.InternalPrefix = flCombine(ArchiveDir,InternalPrefix);
266 Sources.DeLinkLimit = DeLinkLimit;
267 Sources.Stats.DeLinkBytes = Stats.DeLinkBytes;
269 // Create a compressor object
270 MultiCompress Comp(flCombine(ArchiveDir,SrcFile),
271 SrcCompress,Permissions);
272 Sources.Output = Comp.Input;
273 if (_error->PendingError() == true)
274 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
276 c0out << ' ' << BaseDir << ":" << flush;
278 // Do recursive directory searching
279 if (FLFile.empty() == true)
281 if (Sources.RecursiveScan(flCombine(ArchiveDir,BaseDir))== false)
286 if (Sources.LoadFileList(ArchiveDir,FLFile) == false)
289 Sources.Output = 0; // Just in case
291 // Finish compressing
293 if (Comp.Finalize(Size) == false)
296 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
301 << SizeToStr(Size) << "B ";
305 struct timeval NewTime;
306 gettimeofday(&NewTime,0);
307 double Delta = NewTime.tv_sec - StartTime.tv_sec +
308 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
310 c0out << Sources.Stats.Packages << " pkgs in " <<
311 TimeToStr((long)Delta) << endl;
313 Stats.Add(Sources.Stats);
314 Stats.DeLinkBytes = Sources.Stats.DeLinkBytes;
316 return !_error->PendingError();
319 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
320 // ---------------------------------------------------------------------
321 /* This generates the contents file partially described by this object.
322 It searches the given iterator range for other package files that map
323 into this contents file and includes their data as well when building. */
324 bool PackageMap::GenContents(Configuration &Setup,
325 vector<PackageMap>::iterator Begin,
326 vector<PackageMap>::iterator End,
329 if (Contents.empty() == true)
335 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
336 string CacheDir = Setup.FindDir("Dir::CacheDir");
337 string OverrideDir = Setup.FindDir("Dir::OverrideDir");
339 struct timeval StartTime;
340 gettimeofday(&StartTime,0);
342 // Create a package writer object.
343 ContentsWriter Contents("");
344 if (PkgExt.empty() == false && Contents.SetExts(PkgExt) == false)
345 return _error->Error(_("Package extension list is too long"));
346 if (_error->PendingError() == true)
349 MultiCompress Comp(flCombine(ArchiveDir,this->Contents),
350 CntCompress,Permissions);
351 Comp.UpdateMTime = Setup.FindI("Default::ContentsAge",10)*24*60*60;
352 Contents.Output = Comp.Input;
353 if (_error->PendingError() == true)
356 // Write the header out.
357 if (ContentsHead.empty() == false)
359 FileFd Head(flCombine(OverrideDir,ContentsHead),FileFd::ReadOnly);
360 if (_error->PendingError() == true)
363 unsigned long Size = Head.Size();
364 unsigned char Buf[4096];
367 unsigned long ToRead = Size;
368 if (Size > sizeof(Buf))
369 ToRead = sizeof(Buf);
371 if (Head.Read(Buf,ToRead) == false)
374 if (fwrite(Buf,1,ToRead,Comp.Input) != ToRead)
375 return _error->Errno("fwrite",_("Error writing header to contents file"));
381 /* Go over all the package file records and parse all the package
382 files associated with this contents file into one great big honking
383 memory structure, then dump the sorted version */
384 c0out << ' ' << this->Contents << ":" << flush;
385 for (vector<PackageMap>::iterator I = Begin; I != End; I++)
387 if (I->Contents != this->Contents)
390 Contents.Prefix = ArchiveDir;
391 Contents.ReadyDB(flCombine(CacheDir,I->BinCacheDB));
392 Contents.ReadFromPkgs(flCombine(ArchiveDir,I->PkgFile),
395 I->ContentsDone = true;
400 // Finish compressing
402 if (Comp.Finalize(Size) == false || _error->PendingError() == true)
405 return _error->Error(_("Error processing contents %s"),
406 this->Contents.c_str());
411 c0out << " New " << SizeToStr(Size) << "B ";
420 struct timeval NewTime;
421 gettimeofday(&NewTime,0);
422 double Delta = NewTime.tv_sec - StartTime.tv_sec +
423 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
425 c0out << Contents.Stats.Packages << " files " <<
426 SizeToStr(Contents.Stats.Bytes) << "B " <<
427 TimeToStr((long)Delta) << endl;
433 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
434 // ---------------------------------------------------------------------
435 /* This populates the PkgList with all the possible permutations of the
436 section/arch lists. */
437 void LoadTree(vector<PackageMap> &PkgList,Configuration &Setup)
440 string DDir = Setup.Find("TreeDefault::Directory",
441 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
442 string DSDir = Setup.Find("TreeDefault::SrcDirectory",
443 "$(DIST)/$(SECTION)/source/");
444 string DPkg = Setup.Find("TreeDefault::Packages",
445 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
446 string DIPrfx = Setup.Find("TreeDefault::InternalPrefix",
447 "$(DIST)/$(SECTION)/");
448 string DContents = Setup.Find("TreeDefault::Contents",
449 "$(DIST)/Contents-$(ARCH)");
450 string DContentsH = Setup.Find("TreeDefault::Contents::Header","");
451 string DBCache = Setup.Find("TreeDefault::BinCacheDB",
452 "packages-$(ARCH).db");
453 string DSources = Setup.Find("TreeDefault::Sources",
454 "$(DIST)/$(SECTION)/source/Sources");
455 string DFLFile = Setup.Find("TreeDefault::FileList", "");
456 string DSFLFile = Setup.Find("TreeDefault::SourceFileList", "");
458 // Process 'tree' type sections
459 const Configuration::Item *Top = Setup.Tree("tree");
460 for (Top = (Top == 0?0:Top->Child); Top != 0;)
462 Configuration Block(Top);
463 string Dist = Top->Tag;
465 // Parse the sections
466 string Tmp = Block.Find("Sections");
467 const char *Sections = Tmp.c_str();
469 while (ParseQuoteWord(Sections,Section) == true)
471 string Tmp2 = Block.Find("Architectures");
473 const char *Archs = Tmp2.c_str();
474 while (ParseQuoteWord(Archs,Arch) == true)
476 struct SubstVar Vars[] = {{"$(DIST)",&Dist},
477 {"$(SECTION)",&Section},
482 Itm.BinOverride = SubstVar(Block.Find("BinOverride"),Vars);
483 Itm.InternalPrefix = SubstVar(Block.Find("InternalPrefix",DIPrfx.c_str()),Vars);
485 if (stringcasecmp(Arch,"source") == 0)
487 Itm.SrcOverride = SubstVar(Block.Find("SrcOverride"),Vars);
488 Itm.BaseDir = SubstVar(Block.Find("SrcDirectory",DSDir.c_str()),Vars);
489 Itm.SrcFile = SubstVar(Block.Find("Sources",DSources.c_str()),Vars);
490 Itm.Tag = SubstVar("$(DIST)/$(SECTION)/source",Vars);
491 Itm.FLFile = SubstVar(Block.Find("SourceFileList",DSFLFile.c_str()),Vars);
492 Itm.SrcExtraOverride = SubstVar(Block.Find("SrcExtraOverride"),Vars);
496 Itm.BinCacheDB = SubstVar(Block.Find("BinCacheDB",DBCache.c_str()),Vars);
497 Itm.BaseDir = SubstVar(Block.Find("Directory",DDir.c_str()),Vars);
498 Itm.PkgFile = SubstVar(Block.Find("Packages",DPkg.c_str()),Vars);
499 Itm.Tag = SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars);
501 Itm.Contents = SubstVar(Block.Find("Contents",DContents.c_str()),Vars);
502 Itm.ContentsHead = SubstVar(Block.Find("Contents::Header",DContentsH.c_str()),Vars);
503 Itm.FLFile = SubstVar(Block.Find("FileList",DFLFile.c_str()),Vars);
504 Itm.ExtraOverride = SubstVar(Block.Find("ExtraOverride"),Vars);
507 Itm.GetGeneral(Setup,Block);
508 PkgList.push_back(Itm);
516 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
517 // ---------------------------------------------------------------------
519 void LoadBinDir(vector<PackageMap> &PkgList,Configuration &Setup)
521 // Process 'bindirectory' type sections
522 const Configuration::Item *Top = Setup.Tree("bindirectory");
523 for (Top = (Top == 0?0:Top->Child); Top != 0;)
525 Configuration Block(Top);
528 Itm.PkgFile = Block.Find("Packages");
529 Itm.SrcFile = Block.Find("Sources");
530 Itm.BinCacheDB = Block.Find("BinCacheDB");
531 Itm.BinOverride = Block.Find("BinOverride");
532 Itm.ExtraOverride = Block.Find("ExtraOverride");
533 Itm.SrcExtraOverride = Block.Find("SrcExtraOverride");
534 Itm.SrcOverride = Block.Find("SrcOverride");
535 Itm.BaseDir = Top->Tag;
536 Itm.FLFile = Block.Find("FileList");
537 Itm.InternalPrefix = Block.Find("InternalPrefix",Top->Tag.c_str());
538 Itm.Contents = Block.Find("Contents");
539 Itm.ContentsHead = Block.Find("Contents::Header");
541 Itm.GetGeneral(Setup,Block);
542 PkgList.push_back(Itm);
549 // ShowHelp - Show the help text /*{{{*/
550 // ---------------------------------------------------------------------
552 bool ShowHelp(CommandLine &CmdL)
554 ioprintf(cout,_("%s %s for %s %s compiled on %s %s\n"),PACKAGE,VERSION,
555 COMMON_OS,COMMON_CPU,__DATE__,__TIME__);
556 if (_config->FindB("version") == true)
560 _("Usage: apt-ftparchive [options] command\n"
561 "Commands: packages binarypath [overridefile [pathprefix]]\n"
562 " sources srcpath [overridefile [pathprefix]]\n"
565 " generate config [groups]\n"
568 "apt-ftparchive generates index files for Debian archives. It supports\n"
569 "many styles of generation from fully automated to functional replacements\n"
570 "for dpkg-scanpackages and dpkg-scansources\n"
572 "apt-ftparchive generates Package files from a tree of .debs. The\n"
573 "Package file contains the contents of all the control fields from\n"
574 "each package as well as the MD5 hash and filesize. An override file\n"
575 "is supported to force the value of Priority and Section.\n"
577 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
578 "The --source-override option can be used to specify a src override file\n"
580 "The 'packages' and 'sources' command should be run in the root of the\n"
581 "tree. BinaryPath should point to the base of the recursive search and \n"
582 "override file should contain the override flags. Pathprefix is\n"
583 "appended to the filename fields if present. Example usage from the \n"
585 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
586 " dists/potato/main/binary-i386/Packages\n"
589 " -h This help text\n"
590 " --md5 Control MD5 generation\n"
591 " -s=? Source override file\n"
593 " -d=? Select the optional caching database\n"
594 " --no-delink Enable delinking debug mode\n"
595 " --contents Control contents file generation\n"
596 " -c=? Read this configuration file\n"
597 " -o=? Set an arbitrary configuration option") << endl;
602 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
603 // ---------------------------------------------------------------------
604 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
605 bool SimpleGenPackages(CommandLine &CmdL)
607 if (CmdL.FileSize() < 2)
608 return ShowHelp(CmdL);
611 if (CmdL.FileSize() >= 3)
612 Override = CmdL.FileList[2];
614 // Create a package writer object.
615 PackagesWriter Packages(_config->Find("APT::FTPArchive::DB"),
617 if (_error->PendingError() == true)
620 if (CmdL.FileSize() >= 4)
621 Packages.PathPrefix = CmdL.FileList[3];
623 // Do recursive directory searching
624 if (Packages.RecursiveScan(CmdL.FileList[1]) == false)
630 // SimpleGenContents - Generate a Contents listing /*{{{*/
631 // ---------------------------------------------------------------------
633 bool SimpleGenContents(CommandLine &CmdL)
635 if (CmdL.FileSize() < 2)
636 return ShowHelp(CmdL);
638 // Create a package writer object.
639 ContentsWriter Contents(_config->Find("APT::FTPArchive::DB"));
640 if (_error->PendingError() == true)
643 // Do recursive directory searching
644 if (Contents.RecursiveScan(CmdL.FileList[1]) == false)
652 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
653 // ---------------------------------------------------------------------
654 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
655 bool SimpleGenSources(CommandLine &CmdL)
657 if (CmdL.FileSize() < 2)
658 return ShowHelp(CmdL);
661 if (CmdL.FileSize() >= 3)
662 Override = CmdL.FileList[2];
665 if (Override.empty() == false)
666 SOverride = Override + ".src";
668 SOverride = _config->Find("APT::FTPArchive::SourceOverride",
671 // Create a package writer object.
672 SourcesWriter Sources(Override,SOverride);
673 if (_error->PendingError() == true)
676 if (CmdL.FileSize() >= 4)
677 Sources.PathPrefix = CmdL.FileList[3];
679 // Do recursive directory searching
680 if (Sources.RecursiveScan(CmdL.FileList[1]) == false)
686 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
687 // ---------------------------------------------------------------------
688 bool SimpleGenRelease(CommandLine &CmdL)
690 if (CmdL.FileSize() < 2)
691 return ShowHelp(CmdL);
693 string Dir = CmdL.FileList[1];
695 ReleaseWriter Release("");
696 Release.DirStrip = Dir;
698 if (_error->PendingError() == true)
701 if (Release.RecursiveScan(Dir) == false)
710 // Generate - Full generate, using a config file /*{{{*/
711 // ---------------------------------------------------------------------
713 bool Generate(CommandLine &CmdL)
715 struct CacheDB::Stats SrcStats;
716 if (CmdL.FileSize() < 2)
717 return ShowHelp(CmdL);
719 struct timeval StartTime;
720 gettimeofday(&StartTime,0);
721 struct CacheDB::Stats Stats;
723 // Read the configuration file.
725 if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
728 vector<PackageMap> PkgList;
729 LoadTree(PkgList,Setup);
730 LoadBinDir(PkgList,Setup);
732 // Sort by cache DB to improve IO locality.
733 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
736 if (CmdL.FileSize() <= 2)
738 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
739 if (I->GenPackages(Setup,Stats) == false)
740 _error->DumpErrors();
741 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
742 if (I->GenSources(Setup,SrcStats) == false)
743 _error->DumpErrors();
747 // Make a choice list out of the package list..
748 RxChoiceList *List = new RxChoiceList[2*PkgList.size()+1];
749 RxChoiceList *End = List;
750 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
752 End->UserData = &(*I);
753 End->Str = I->BaseDir.c_str();
756 End->UserData = &(*I);
757 End->Str = I->Tag.c_str();
763 if (RegexChoice(List,CmdL.FileList + 2,CmdL.FileList + CmdL.FileSize()) == 0)
766 return _error->Error(_("No selections matched"));
768 _error->DumpErrors();
770 // Do the generation for Packages
771 for (End = List; End->Str != 0; End++)
773 if (End->Hit == false)
776 PackageMap *I = (PackageMap *)End->UserData;
777 if (I->PkgDone == true)
779 if (I->GenPackages(Setup,Stats) == false)
780 _error->DumpErrors();
783 // Do the generation for Sources
784 for (End = List; End->Str != 0; End++)
786 if (End->Hit == false)
789 PackageMap *I = (PackageMap *)End->UserData;
790 if (I->SrcDone == true)
792 if (I->GenSources(Setup,SrcStats) == false)
793 _error->DumpErrors();
799 if (_config->FindB("APT::FTPArchive::Contents",true) == false)
802 c1out << "Done Packages, Starting contents." << endl;
804 // Sort the contents file list by date
805 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
806 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
809 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),
810 I->CntCompress,A) == false)
811 time(&I->ContentsMTime);
813 I->ContentsMTime = A.st_mtime;
815 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::ContentsCompare());
817 /* Now for Contents.. The process here is to do a make-like dependency
818 check. Each contents file is verified to be newer than the package files
819 that describe the debs it indexes. Since the package files contain
820 hashes of the .debs this means they have not changed either so the
821 contents must be up to date. */
822 unsigned long MaxContentsChange = Setup.FindI("Default::MaxContentsChange",UINT_MAX)*1024;
823 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
825 // This record is not relevent
826 if (I->ContentsDone == true ||
827 I->Contents.empty() == true)
830 // Do not do everything if the user specified sections.
831 if (CmdL.FileSize() > 2 && I->PkgDone == false)
835 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),I->CntCompress,A) == true)
837 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->PkgFile),I->PkgCompress,B) == false)
839 _error->Warning(_("Some files are missing in the package file group `%s'"),I->PkgFile.c_str());
843 if (A.st_mtime > B.st_mtime)
847 if (I->GenContents(Setup,PkgList.begin(),PkgList.end(),
848 MaxContentsChange) == false)
849 _error->DumpErrors();
852 if (MaxContentsChange == 0)
854 c1out << "Hit contents update byte limit" << endl;
859 struct timeval NewTime;
860 gettimeofday(&NewTime,0);
861 double Delta = NewTime.tv_sec - StartTime.tv_sec +
862 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
863 c1out << "Done. " << SizeToStr(Stats.Bytes) << "B in " << Stats.Packages
864 << " archives. Took " << TimeToStr((long)Delta) << endl;
869 // Clean - Clean out the databases /*{{{*/
870 // ---------------------------------------------------------------------
872 bool Clean(CommandLine &CmdL)
874 if (CmdL.FileSize() != 2)
875 return ShowHelp(CmdL);
877 // Read the configuration file.
879 if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
882 vector<PackageMap> PkgList;
883 LoadTree(PkgList,Setup);
884 LoadBinDir(PkgList,Setup);
886 // Sort by cache DB to improve IO locality.
887 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
889 string CacheDir = Setup.FindDir("Dir::CacheDir");
891 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); )
893 c0out << I->BinCacheDB << endl;
894 CacheDB DB(flCombine(CacheDir,I->BinCacheDB));
895 if (DB.Clean() == false)
896 _error->DumpErrors();
898 string CacheDB = I->BinCacheDB;
899 for (; I != PkgList.end() && I->BinCacheDB == CacheDB; I++);
906 int main(int argc, const char *argv[])
908 #if !defined(__ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__) || __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ < 10200
910 memset(nl, 0, sizeof(nl));
911 nl[0].n_un.n_name = (char *) "_useMDNSResponder";
912 nlist("/usr/lib/libc.dylib", nl);
913 if (nl[0].n_type != N_UNDF)
914 *(int *) nl[0].n_value = 0;
917 CommandLine::Args Args[] = {
918 {'h',"help","help",0},
919 {0,"md5","APT::FTPArchive::MD5",0},
920 {'v',"version","version",0},
921 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg},
922 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg},
923 {'q',"quiet","quiet",CommandLine::IntLevel},
924 {'q',"silent","quiet",CommandLine::IntLevel},
925 {0,"delink","APT::FTPArchive::DeLinkAct",0},
926 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
927 {0,"contents","APT::FTPArchive::Contents",0},
928 {'c',"config-file",0,CommandLine::ConfigFile},
929 {'o',"option",0,CommandLine::ArbItem},
931 CommandLine::Dispatch Cmds[] = {{"packages",&SimpleGenPackages},
932 {"contents",&SimpleGenContents},
933 {"sources",&SimpleGenSources},
934 {"release",&SimpleGenRelease},
935 {"generate",&Generate},
940 // Parse the command line and initialize the package library
941 CommandLine CmdL(Args,_config);
942 if (CmdL.Parse(argc,argv) == false)
944 _error->DumpErrors();
948 // See if the help should be shown
949 if (_config->FindB("help") == true ||
950 _config->FindB("version") == true ||
951 CmdL.FileSize() == 0)
957 // Setup the output streams
958 c0out.rdbuf(clog.rdbuf());
959 c1out.rdbuf(clog.rdbuf());
960 c2out.rdbuf(clog.rdbuf());
961 Quiet = _config->FindI("quiet",0);
963 c0out.rdbuf(devnull.rdbuf());
965 c1out.rdbuf(devnull.rdbuf());
967 // Match the operation
968 CmdL.DispatchArg(Cmds);
970 if (_error->empty() == false)
972 bool Errors = _error->PendingError();
973 _error->DumpErrors();
974 return Errors == true?100:0;