]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-ftparchive.cc,v 1.8.2.3 2004/01/02 22:01:48 mdz Exp $
4 /* ######################################################################
6 apt-ftparchive - Efficient work-alike for dpkg-scanpackages
8 Let contents be disabled from the conf
10 ##################################################################### */
12 // Include Files /*{{{*/
13 #include "apt-ftparchive.h"
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/configuration.h>
17 #include <apt-pkg/cmndline.h>
18 #include <apt-pkg/strutl.h>
19 #include <apt-pkg/init.h>
29 #include "multicompress.h"
37 ofstream
devnull("/dev/null");
40 // struct PackageMap - List of all package files in the config file /*{{{*/
41 // ---------------------------------------------------------------------
47 string InternalPrefix
;
52 // Stuff for the Package File
58 // We generate for this given arch
61 // Stuff for the Source File
64 string SrcExtraOverride
;
76 unsigned int DeLinkLimit
;
84 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
86 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
87 {return x
.ContentsMTime
< y
.ContentsMTime
;};
90 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
92 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
93 {return x
.BinCacheDB
< y
.BinCacheDB
;};
96 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
97 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
98 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
99 bool GenContents(Configuration
&Setup
,
100 vector
<PackageMap
>::iterator Begin
,
101 vector
<PackageMap
>::iterator End
,
102 unsigned long &Left
);
104 PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
105 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
109 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
110 // ---------------------------------------------------------------------
112 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
114 PathPrefix
= Block
.Find("PathPrefix");
116 if (Block
.FindB("External-Links",true) == false)
117 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
121 PkgCompress
= Block
.Find("Packages::Compress",
122 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
123 CntCompress
= Block
.Find("Contents::Compress",
124 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
125 SrcCompress
= Block
.Find("Sources::Compress",
126 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
128 SrcExt
= Block
.Find("Sources::Extensions",
129 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
130 PkgExt
= Block
.Find("Packages::Extensions",
131 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
133 Permissions
= Setup
.FindI("Default::FileMode",0644);
135 if (FLFile
.empty() == false)
136 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
142 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
143 // ---------------------------------------------------------------------
144 /* This generates the Package File described by this object. */
145 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
147 if (PkgFile
.empty() == true)
150 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
151 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
152 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
154 struct timeval StartTime
;
155 gettimeofday(&StartTime
,0);
159 // Create a package writer object.
160 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
161 flCombine(OverrideDir
,BinOverride
),
162 flCombine(OverrideDir
,ExtraOverride
),
164 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
165 return _error
->Error(_("Package extension list is too long"));
166 if (_error
->PendingError() == true)
167 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
169 Packages
.PathPrefix
= PathPrefix
;
170 Packages
.DirStrip
= ArchiveDir
;
171 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
173 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
174 Packages
.DeLinkLimit
= DeLinkLimit
;
176 // Create a compressor object
177 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
178 PkgCompress
,Permissions
);
179 Packages
.Output
= Comp
.Input
;
180 if (_error
->PendingError() == true)
181 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
183 c0out
<< ' ' << BaseDir
<< ":" << flush
;
185 // Do recursive directory searching
186 if (FLFile
.empty() == true)
188 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
193 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
197 Packages
.Output
= 0; // Just in case
199 // Finish compressing
201 if (Comp
.Finalize(Size
) == false)
204 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
209 << SizeToStr(Size
) << "B ";
213 struct timeval NewTime
;
214 gettimeofday(&NewTime
,0);
215 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
216 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
218 c0out
<< Packages
.Stats
.Packages
<< " files " <<
219 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
220 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
221 TimeToStr((long)Delta
) << endl
;
223 Stats
.Add(Packages
.Stats
);
224 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
226 return !_error
->PendingError();
230 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
231 // ---------------------------------------------------------------------
232 /* This generates the Sources File described by this object. */
233 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
235 if (SrcFile
.empty() == true)
238 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
239 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
240 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
242 struct timeval StartTime
;
243 gettimeofday(&StartTime
,0);
247 // Create a package writer object.
248 SourcesWriter
Sources(flCombine(OverrideDir
,BinOverride
),
249 flCombine(OverrideDir
,SrcOverride
),
250 flCombine(OverrideDir
,SrcExtraOverride
));
251 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
252 return _error
->Error(_("Source extension list is too long"));
253 if (_error
->PendingError() == true)
254 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
256 Sources
.PathPrefix
= PathPrefix
;
257 Sources
.DirStrip
= ArchiveDir
;
258 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
260 Sources
.DeLinkLimit
= DeLinkLimit
;
261 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
263 // Create a compressor object
264 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
265 SrcCompress
,Permissions
);
266 Sources
.Output
= Comp
.Input
;
267 if (_error
->PendingError() == true)
268 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
270 c0out
<< ' ' << BaseDir
<< ":" << flush
;
272 // Do recursive directory searching
273 if (FLFile
.empty() == true)
275 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
280 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
283 Sources
.Output
= 0; // Just in case
285 // Finish compressing
287 if (Comp
.Finalize(Size
) == false)
290 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
295 << SizeToStr(Size
) << "B ";
299 struct timeval NewTime
;
300 gettimeofday(&NewTime
,0);
301 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
302 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
304 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
305 TimeToStr((long)Delta
) << endl
;
307 Stats
.Add(Sources
.Stats
);
308 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
310 return !_error
->PendingError();
313 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
314 // ---------------------------------------------------------------------
315 /* This generates the contents file partially described by this object.
316 It searches the given iterator range for other package files that map
317 into this contents file and includes their data as well when building. */
318 bool PackageMap::GenContents(Configuration
&Setup
,
319 vector
<PackageMap
>::iterator Begin
,
320 vector
<PackageMap
>::iterator End
,
323 if (Contents
.empty() == true)
329 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
330 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
331 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
333 struct timeval StartTime
;
334 gettimeofday(&StartTime
,0);
336 // Create a package writer object.
337 ContentsWriter
Contents("", Arch
);
338 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
339 return _error
->Error(_("Package extension list is too long"));
340 if (_error
->PendingError() == true)
343 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
344 CntCompress
,Permissions
);
345 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
346 Contents
.Output
= Comp
.Input
;
347 if (_error
->PendingError() == true)
350 // Write the header out.
351 if (ContentsHead
.empty() == false)
353 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
354 if (_error
->PendingError() == true)
357 unsigned long Size
= Head
.Size();
358 unsigned char Buf
[4096];
361 unsigned long ToRead
= Size
;
362 if (Size
> sizeof(Buf
))
363 ToRead
= sizeof(Buf
);
365 if (Head
.Read(Buf
,ToRead
) == false)
368 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
369 return _error
->Errno("fwrite",_("Error writing header to contents file"));
375 /* Go over all the package file records and parse all the package
376 files associated with this contents file into one great big honking
377 memory structure, then dump the sorted version */
378 c0out
<< ' ' << this->Contents
<< ":" << flush
;
379 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; I
++)
381 if (I
->Contents
!= this->Contents
)
384 Contents
.Prefix
= ArchiveDir
;
385 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
386 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
389 I
->ContentsDone
= true;
394 // Finish compressing
396 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
399 return _error
->Error(_("Error processing contents %s"),
400 this->Contents
.c_str());
405 c0out
<< " New " << SizeToStr(Size
) << "B ";
414 struct timeval NewTime
;
415 gettimeofday(&NewTime
,0);
416 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
417 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
419 c0out
<< Contents
.Stats
.Packages
<< " files " <<
420 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
421 TimeToStr((long)Delta
) << endl
;
427 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
428 // ---------------------------------------------------------------------
429 /* This populates the PkgList with all the possible permutations of the
430 section/arch lists. */
431 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
434 string DDir
= Setup
.Find("TreeDefault::Directory",
435 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
436 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
437 "$(DIST)/$(SECTION)/source/");
438 string DPkg
= Setup
.Find("TreeDefault::Packages",
439 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
440 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
441 "$(DIST)/$(SECTION)/");
442 string DContents
= Setup
.Find("TreeDefault::Contents",
443 "$(DIST)/Contents-$(ARCH)");
444 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
445 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
446 "packages-$(ARCH).db");
447 string DSources
= Setup
.Find("TreeDefault::Sources",
448 "$(DIST)/$(SECTION)/source/Sources");
449 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
450 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
452 // Process 'tree' type sections
453 const Configuration::Item
*Top
= Setup
.Tree("tree");
454 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
456 Configuration
Block(Top
);
457 string Dist
= Top
->Tag
;
459 // Parse the sections
460 string Tmp
= Block
.Find("Sections");
461 const char *Sections
= Tmp
.c_str();
463 while (ParseQuoteWord(Sections
,Section
) == true)
465 string Tmp2
= Block
.Find("Architectures");
467 const char *Archs
= Tmp2
.c_str();
468 while (ParseQuoteWord(Archs
,Arch
) == true)
470 struct SubstVar Vars
[] = {{"$(DIST)",&Dist
},
471 {"$(SECTION)",&Section
},
476 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
477 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
479 if (stringcasecmp(Arch
,"source") == 0)
481 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
482 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
483 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
484 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
485 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
486 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
490 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
491 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
492 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
493 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
495 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
496 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
497 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
498 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
501 Itm
.GetGeneral(Setup
,Block
);
502 PkgList
.push_back(Itm
);
510 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
511 // ---------------------------------------------------------------------
513 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
515 // Process 'bindirectory' type sections
516 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
517 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
519 Configuration
Block(Top
);
522 Itm
.PkgFile
= Block
.Find("Packages");
523 Itm
.SrcFile
= Block
.Find("Sources");
524 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
525 Itm
.BinOverride
= Block
.Find("BinOverride");
526 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
527 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
528 Itm
.SrcOverride
= Block
.Find("SrcOverride");
529 Itm
.BaseDir
= Top
->Tag
;
530 Itm
.FLFile
= Block
.Find("FileList");
531 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
532 Itm
.Contents
= Block
.Find("Contents");
533 Itm
.ContentsHead
= Block
.Find("Contents::Header");
535 Itm
.GetGeneral(Setup
,Block
);
536 PkgList
.push_back(Itm
);
543 // ShowHelp - Show the help text /*{{{*/
544 // ---------------------------------------------------------------------
546 bool ShowHelp(CommandLine
&CmdL
)
548 ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,VERSION
,
549 COMMON_ARCH
,__DATE__
,__TIME__
);
550 if (_config
->FindB("version") == true)
554 _("Usage: apt-ftparchive [options] command\n"
555 "Commands: packages binarypath [overridefile [pathprefix]]\n"
556 " sources srcpath [overridefile [pathprefix]]\n"
559 " generate config [groups]\n"
562 "apt-ftparchive generates index files for Debian archives. It supports\n"
563 "many styles of generation from fully automated to functional replacements\n"
564 "for dpkg-scanpackages and dpkg-scansources\n"
566 "apt-ftparchive generates Package files from a tree of .debs. The\n"
567 "Package file contains the contents of all the control fields from\n"
568 "each package as well as the MD5 hash and filesize. An override file\n"
569 "is supported to force the value of Priority and Section.\n"
571 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
572 "The --source-override option can be used to specify a src override file\n"
574 "The 'packages' and 'sources' command should be run in the root of the\n"
575 "tree. BinaryPath should point to the base of the recursive search and \n"
576 "override file should contain the override flags. Pathprefix is\n"
577 "appended to the filename fields if present. Example usage from the \n"
579 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
580 " dists/potato/main/binary-i386/Packages\n"
583 " -h This help text\n"
584 " --md5 Control MD5 generation\n"
585 " -s=? Source override file\n"
587 " -d=? Select the optional caching database\n"
588 " --no-delink Enable delinking debug mode\n"
589 " --contents Control contents file generation\n"
590 " -c=? Read this configuration file\n"
591 " -o=? Set an arbitrary configuration option") << endl
;
596 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
597 // ---------------------------------------------------------------------
598 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
599 bool SimpleGenPackages(CommandLine
&CmdL
)
601 if (CmdL
.FileSize() < 2)
602 return ShowHelp(CmdL
);
605 if (CmdL
.FileSize() >= 3)
606 Override
= CmdL
.FileList
[2];
608 // Create a package writer object.
609 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
610 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
611 if (_error
->PendingError() == true)
614 if (CmdL
.FileSize() >= 4)
615 Packages
.PathPrefix
= CmdL
.FileList
[3];
617 // Do recursive directory searching
618 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
624 // SimpleGenContents - Generate a Contents listing /*{{{*/
625 // ---------------------------------------------------------------------
627 bool SimpleGenContents(CommandLine
&CmdL
)
629 if (CmdL
.FileSize() < 2)
630 return ShowHelp(CmdL
);
632 // Create a package writer object.
633 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
634 if (_error
->PendingError() == true)
637 // Do recursive directory searching
638 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
646 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
647 // ---------------------------------------------------------------------
648 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
649 bool SimpleGenSources(CommandLine
&CmdL
)
651 if (CmdL
.FileSize() < 2)
652 return ShowHelp(CmdL
);
655 if (CmdL
.FileSize() >= 3)
656 Override
= CmdL
.FileList
[2];
659 if (Override
.empty() == false)
660 SOverride
= Override
+ ".src";
662 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
665 // Create a package writer object.
666 SourcesWriter
Sources(Override
,SOverride
);
667 if (_error
->PendingError() == true)
670 if (CmdL
.FileSize() >= 4)
671 Sources
.PathPrefix
= CmdL
.FileList
[3];
673 // Do recursive directory searching
674 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
680 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
681 // ---------------------------------------------------------------------
682 bool SimpleGenRelease(CommandLine
&CmdL
)
684 if (CmdL
.FileSize() < 2)
685 return ShowHelp(CmdL
);
687 string Dir
= CmdL
.FileList
[1];
689 ReleaseWriter
Release("");
690 Release
.DirStrip
= Dir
;
692 if (_error
->PendingError() == true)
695 if (Release
.RecursiveScan(Dir
) == false)
704 // Generate - Full generate, using a config file /*{{{*/
705 // ---------------------------------------------------------------------
707 bool Generate(CommandLine
&CmdL
)
709 struct CacheDB::Stats SrcStats
;
710 if (CmdL
.FileSize() < 2)
711 return ShowHelp(CmdL
);
713 struct timeval StartTime
;
714 gettimeofday(&StartTime
,0);
715 struct CacheDB::Stats Stats
;
717 // Read the configuration file.
719 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
722 vector
<PackageMap
> PkgList
;
723 LoadTree(PkgList
,Setup
);
724 LoadBinDir(PkgList
,Setup
);
726 // Sort by cache DB to improve IO locality.
727 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
730 if (CmdL
.FileSize() <= 2)
732 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
733 if (I
->GenPackages(Setup
,Stats
) == false)
734 _error
->DumpErrors();
735 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
736 if (I
->GenSources(Setup
,SrcStats
) == false)
737 _error
->DumpErrors();
741 // Make a choice list out of the package list..
742 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
743 RxChoiceList
*End
= List
;
744 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
746 End
->UserData
= &(*I
);
747 End
->Str
= I
->BaseDir
.c_str();
750 End
->UserData
= &(*I
);
751 End
->Str
= I
->Tag
.c_str();
757 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
760 return _error
->Error(_("No selections matched"));
762 _error
->DumpErrors();
764 // Do the generation for Packages
765 for (End
= List
; End
->Str
!= 0; End
++)
767 if (End
->Hit
== false)
770 PackageMap
*I
= (PackageMap
*)End
->UserData
;
771 if (I
->PkgDone
== true)
773 if (I
->GenPackages(Setup
,Stats
) == false)
774 _error
->DumpErrors();
777 // Do the generation for Sources
778 for (End
= List
; End
->Str
!= 0; End
++)
780 if (End
->Hit
== false)
783 PackageMap
*I
= (PackageMap
*)End
->UserData
;
784 if (I
->SrcDone
== true)
786 if (I
->GenSources(Setup
,SrcStats
) == false)
787 _error
->DumpErrors();
793 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
796 c1out
<< "Packages done, Starting contents." << endl
;
798 // Sort the contents file list by date
799 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
800 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
803 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
804 I
->CntCompress
,A
) == false)
805 time(&I
->ContentsMTime
);
807 I
->ContentsMTime
= A
.st_mtime
;
809 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
811 /* Now for Contents.. The process here is to do a make-like dependency
812 check. Each contents file is verified to be newer than the package files
813 that describe the debs it indexes. Since the package files contain
814 hashes of the .debs this means they have not changed either so the
815 contents must be up to date. */
816 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
817 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
819 // This record is not relevent
820 if (I
->ContentsDone
== true ||
821 I
->Contents
.empty() == true)
824 // Do not do everything if the user specified sections.
825 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
829 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
831 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
833 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
837 if (A
.st_mtime
> B
.st_mtime
)
841 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
842 MaxContentsChange
) == false)
843 _error
->DumpErrors();
846 if (MaxContentsChange
== 0)
848 c1out
<< "Hit contents update byte limit" << endl
;
853 struct timeval NewTime
;
854 gettimeofday(&NewTime
,0);
855 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
856 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
857 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
858 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
863 // Clean - Clean out the databases /*{{{*/
864 // ---------------------------------------------------------------------
866 bool Clean(CommandLine
&CmdL
)
868 if (CmdL
.FileSize() != 2)
869 return ShowHelp(CmdL
);
871 // Read the configuration file.
873 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
876 vector
<PackageMap
> PkgList
;
877 LoadTree(PkgList
,Setup
);
878 LoadBinDir(PkgList
,Setup
);
880 // Sort by cache DB to improve IO locality.
881 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
883 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
885 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
887 c0out
<< I
->BinCacheDB
<< endl
;
888 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
889 if (DB
.Clean() == false)
890 _error
->DumpErrors();
892 string CacheDB
= I
->BinCacheDB
;
893 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; I
++);
900 int main(int argc
, const char *argv
[])
902 setlocale(LC_ALL
, "");
903 CommandLine::Args Args
[] = {
904 {'h',"help","help",0},
905 {0,"md5","APT::FTPArchive::MD5",0},
906 {'v',"version","version",0},
907 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
908 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
909 {'q',"quiet","quiet",CommandLine::IntLevel
},
910 {'q',"silent","quiet",CommandLine::IntLevel
},
911 {0,"delink","APT::FTPArchive::DeLinkAct",0},
912 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
913 {0,"contents","APT::FTPArchive::Contents",0},
914 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
915 {'c',"config-file",0,CommandLine::ConfigFile
},
916 {'o',"option",0,CommandLine::ArbItem
},
918 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
919 {"contents",&SimpleGenContents
},
920 {"sources",&SimpleGenSources
},
921 {"release",&SimpleGenRelease
},
922 {"generate",&Generate
},
927 // Parse the command line and initialize the package library
928 CommandLine
CmdL(Args
,_config
);
929 if (pkgInitConfig(*_config
) == false || CmdL
.Parse(argc
,argv
) == false)
931 _error
->DumpErrors();
935 // See if the help should be shown
936 if (_config
->FindB("help") == true ||
937 _config
->FindB("version") == true ||
938 CmdL
.FileSize() == 0)
944 // Setup the output streams
945 c0out
.rdbuf(clog
.rdbuf());
946 c1out
.rdbuf(clog
.rdbuf());
947 c2out
.rdbuf(clog
.rdbuf());
948 Quiet
= _config
->FindI("quiet",0);
950 c0out
.rdbuf(devnull
.rdbuf());
952 c1out
.rdbuf(devnull
.rdbuf());
954 // Match the operation
955 CmdL
.DispatchArg(Cmds
);
957 if (_error
->empty() == false)
959 bool Errors
= _error
->PendingError();
960 _error
->DumpErrors();
961 return Errors
== true?100:0;