]>
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-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"
39 ofstream
devnull("/dev/null");
42 // struct PackageMap - List of all package files in the config file /*{{{*/
43 // ---------------------------------------------------------------------
49 string InternalPrefix
;
54 // Stuff for the Package File
60 // We generate for this given arch
63 // Stuff for the Source File
66 string SrcExtraOverride
;
78 unsigned int DeLinkLimit
;
86 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
88 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
89 {return x
.ContentsMTime
< y
.ContentsMTime
;};
92 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
94 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
95 {return x
.BinCacheDB
< y
.BinCacheDB
;};
98 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
99 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
100 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
101 bool GenContents(Configuration
&Setup
,
102 vector
<PackageMap
>::iterator Begin
,
103 vector
<PackageMap
>::iterator End
,
104 unsigned long &Left
);
106 PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
107 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
111 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
112 // ---------------------------------------------------------------------
114 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
116 PathPrefix
= Block
.Find("PathPrefix");
118 if (Block
.FindB("External-Links",true) == false)
119 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
123 PkgCompress
= Block
.Find("Packages::Compress",
124 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
125 CntCompress
= Block
.Find("Contents::Compress",
126 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
127 SrcCompress
= Block
.Find("Sources::Compress",
128 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
130 SrcExt
= Block
.Find("Sources::Extensions",
131 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
132 PkgExt
= Block
.Find("Packages::Extensions",
133 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
135 Permissions
= Setup
.FindI("Default::FileMode",0644);
137 if (FLFile
.empty() == false)
138 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
144 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
145 // ---------------------------------------------------------------------
146 /* This generates the Package File described by this object. */
147 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
149 if (PkgFile
.empty() == true)
152 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
153 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
154 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
156 struct timeval StartTime
;
157 gettimeofday(&StartTime
,0);
161 // Create a package writer object.
162 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
163 flCombine(OverrideDir
,BinOverride
),
164 flCombine(OverrideDir
,ExtraOverride
),
166 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
167 return _error
->Error(_("Package extension list is too long"));
168 if (_error
->PendingError() == true)
169 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
171 Packages
.PathPrefix
= PathPrefix
;
172 Packages
.DirStrip
= ArchiveDir
;
173 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
175 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
176 Packages
.DeLinkLimit
= DeLinkLimit
;
178 // Create a compressor object
179 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
180 PkgCompress
,Permissions
);
181 Packages
.Output
= Comp
.Input
;
182 if (_error
->PendingError() == true)
183 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
185 c0out
<< ' ' << BaseDir
<< ":" << flush
;
187 // Do recursive directory searching
188 if (FLFile
.empty() == true)
190 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
195 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
199 Packages
.Output
= 0; // Just in case
201 // Finish compressing
203 if (Comp
.Finalize(Size
) == false)
206 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
211 << SizeToStr(Size
) << "B ";
215 struct timeval NewTime
;
216 gettimeofday(&NewTime
,0);
217 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
218 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
220 c0out
<< Packages
.Stats
.Packages
<< " files " <<
221 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
222 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
223 TimeToStr((long)Delta
) << endl
;
225 Stats
.Add(Packages
.Stats
);
226 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
228 return !_error
->PendingError();
232 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
233 // ---------------------------------------------------------------------
234 /* This generates the Sources File described by this object. */
235 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
237 if (SrcFile
.empty() == true)
240 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
241 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
242 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
244 struct timeval StartTime
;
245 gettimeofday(&StartTime
,0);
249 // Create a package writer object.
250 SourcesWriter
Sources(flCombine(OverrideDir
,BinOverride
),
251 flCombine(OverrideDir
,SrcOverride
),
252 flCombine(OverrideDir
,SrcExtraOverride
));
253 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
254 return _error
->Error(_("Source extension list is too long"));
255 if (_error
->PendingError() == true)
256 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
258 Sources
.PathPrefix
= PathPrefix
;
259 Sources
.DirStrip
= ArchiveDir
;
260 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
262 Sources
.DeLinkLimit
= DeLinkLimit
;
263 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
265 // Create a compressor object
266 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
267 SrcCompress
,Permissions
);
268 Sources
.Output
= Comp
.Input
;
269 if (_error
->PendingError() == true)
270 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
272 c0out
<< ' ' << BaseDir
<< ":" << flush
;
274 // Do recursive directory searching
275 if (FLFile
.empty() == true)
277 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
282 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
285 Sources
.Output
= 0; // Just in case
287 // Finish compressing
289 if (Comp
.Finalize(Size
) == false)
292 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
297 << SizeToStr(Size
) << "B ";
301 struct timeval NewTime
;
302 gettimeofday(&NewTime
,0);
303 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
304 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
306 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
307 TimeToStr((long)Delta
) << endl
;
309 Stats
.Add(Sources
.Stats
);
310 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
312 return !_error
->PendingError();
315 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
316 // ---------------------------------------------------------------------
317 /* This generates the contents file partially described by this object.
318 It searches the given iterator range for other package files that map
319 into this contents file and includes their data as well when building. */
320 bool PackageMap::GenContents(Configuration
&Setup
,
321 vector
<PackageMap
>::iterator Begin
,
322 vector
<PackageMap
>::iterator End
,
325 if (Contents
.empty() == true)
331 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
332 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
333 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
335 struct timeval StartTime
;
336 gettimeofday(&StartTime
,0);
338 // Create a package writer object.
339 ContentsWriter
Contents("");
340 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
341 return _error
->Error(_("Package extension list is too long"));
342 if (_error
->PendingError() == true)
345 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
346 CntCompress
,Permissions
);
347 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
348 Contents
.Output
= Comp
.Input
;
349 if (_error
->PendingError() == true)
352 // Write the header out.
353 if (ContentsHead
.empty() == false)
355 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
356 if (_error
->PendingError() == true)
359 unsigned long Size
= Head
.Size();
360 unsigned char Buf
[4096];
363 unsigned long ToRead
= Size
;
364 if (Size
> sizeof(Buf
))
365 ToRead
= sizeof(Buf
);
367 if (Head
.Read(Buf
,ToRead
) == false)
370 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
371 return _error
->Errno("fwrite",_("Error writing header to contents file"));
377 /* Go over all the package file records and parse all the package
378 files associated with this contents file into one great big honking
379 memory structure, then dump the sorted version */
380 c0out
<< ' ' << this->Contents
<< ":" << flush
;
381 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; I
++)
383 if (I
->Contents
!= this->Contents
)
386 Contents
.Prefix
= ArchiveDir
;
387 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
388 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
391 I
->ContentsDone
= true;
396 // Finish compressing
398 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
401 return _error
->Error(_("Error processing contents %s"),
402 this->Contents
.c_str());
407 c0out
<< " New " << SizeToStr(Size
) << "B ";
416 struct timeval NewTime
;
417 gettimeofday(&NewTime
,0);
418 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
419 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
421 c0out
<< Contents
.Stats
.Packages
<< " files " <<
422 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
423 TimeToStr((long)Delta
) << endl
;
429 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
430 // ---------------------------------------------------------------------
431 /* This populates the PkgList with all the possible permutations of the
432 section/arch lists. */
433 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
436 string DDir
= Setup
.Find("TreeDefault::Directory",
437 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
438 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
439 "$(DIST)/$(SECTION)/source/");
440 string DPkg
= Setup
.Find("TreeDefault::Packages",
441 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
442 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
443 "$(DIST)/$(SECTION)/");
444 string DContents
= Setup
.Find("TreeDefault::Contents",
445 "$(DIST)/Contents-$(ARCH)");
446 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
447 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
448 "packages-$(ARCH).db");
449 string DSources
= Setup
.Find("TreeDefault::Sources",
450 "$(DIST)/$(SECTION)/source/Sources");
451 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
452 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
454 // Process 'tree' type sections
455 const Configuration::Item
*Top
= Setup
.Tree("tree");
456 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
458 Configuration
Block(Top
);
459 string Dist
= Top
->Tag
;
461 // Parse the sections
462 string Tmp
= Block
.Find("Sections");
463 const char *Sections
= Tmp
.c_str();
465 while (ParseQuoteWord(Sections
,Section
) == true)
467 string Tmp2
= Block
.Find("Architectures");
469 const char *Archs
= Tmp2
.c_str();
470 while (ParseQuoteWord(Archs
,Arch
) == true)
472 struct SubstVar Vars
[] = {{"$(DIST)",&Dist
},
473 {"$(SECTION)",&Section
},
478 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
479 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
481 if (stringcasecmp(Arch
,"source") == 0)
483 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
484 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
485 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
486 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
487 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
488 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
492 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
493 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
494 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
495 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
497 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
498 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
499 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
500 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
503 Itm
.GetGeneral(Setup
,Block
);
504 PkgList
.push_back(Itm
);
512 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
513 // ---------------------------------------------------------------------
515 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
517 // Process 'bindirectory' type sections
518 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
519 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
521 Configuration
Block(Top
);
524 Itm
.PkgFile
= Block
.Find("Packages");
525 Itm
.SrcFile
= Block
.Find("Sources");
526 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
527 Itm
.BinOverride
= Block
.Find("BinOverride");
528 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
529 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
530 Itm
.SrcOverride
= Block
.Find("SrcOverride");
531 Itm
.BaseDir
= Top
->Tag
;
532 Itm
.FLFile
= Block
.Find("FileList");
533 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
534 Itm
.Contents
= Block
.Find("Contents");
535 Itm
.ContentsHead
= Block
.Find("Contents::Header");
537 Itm
.GetGeneral(Setup
,Block
);
538 PkgList
.push_back(Itm
);
545 // ShowHelp - Show the help text /*{{{*/
546 // ---------------------------------------------------------------------
548 bool ShowHelp(CommandLine
&CmdL
)
550 ioprintf(cout
,_("%s %s for %s %s compiled on %s %s\n"),PACKAGE
,VERSION
,
551 COMMON_OS
,COMMON_CPU
,__DATE__
,__TIME__
);
552 if (_config
->FindB("version") == true)
556 _("Usage: apt-ftparchive [options] command\n"
557 "Commands: packages binarypath [overridefile [pathprefix]]\n"
558 " sources srcpath [overridefile [pathprefix]]\n"
561 " generate config [groups]\n"
564 "apt-ftparchive generates index files for Debian archives. It supports\n"
565 "many styles of generation from fully automated to functional replacements\n"
566 "for dpkg-scanpackages and dpkg-scansources\n"
568 "apt-ftparchive generates Package files from a tree of .debs. The\n"
569 "Package file contains the contents of all the control fields from\n"
570 "each package as well as the MD5 hash and filesize. An override file\n"
571 "is supported to force the value of Priority and Section.\n"
573 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
574 "The --source-override option can be used to specify a src override file\n"
576 "The 'packages' and 'sources' command should be run in the root of the\n"
577 "tree. BinaryPath should point to the base of the recursive search and \n"
578 "override file should contain the override flags. Pathprefix is\n"
579 "appended to the filename fields if present. Example usage from the \n"
581 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
582 " dists/potato/main/binary-i386/Packages\n"
585 " -h This help text\n"
586 " --md5 Control MD5 generation\n"
587 " -s=? Source override file\n"
589 " -d=? Select the optional caching database\n"
590 " --no-delink Enable delinking debug mode\n"
591 " --contents Control contents file generation\n"
592 " -c=? Read this configuration file\n"
593 " -o=? Set an arbitrary configuration option") << endl
;
598 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
599 // ---------------------------------------------------------------------
600 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
601 bool SimpleGenPackages(CommandLine
&CmdL
)
603 if (CmdL
.FileSize() < 2)
604 return ShowHelp(CmdL
);
607 if (CmdL
.FileSize() >= 3)
608 Override
= CmdL
.FileList
[2];
610 // Create a package writer object.
611 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
613 if (_error
->PendingError() == true)
616 if (CmdL
.FileSize() >= 4)
617 Packages
.PathPrefix
= CmdL
.FileList
[3];
619 // Do recursive directory searching
620 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
626 // SimpleGenContents - Generate a Contents listing /*{{{*/
627 // ---------------------------------------------------------------------
629 bool SimpleGenContents(CommandLine
&CmdL
)
631 if (CmdL
.FileSize() < 2)
632 return ShowHelp(CmdL
);
634 // Create a package writer object.
635 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"));
636 if (_error
->PendingError() == true)
639 // Do recursive directory searching
640 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
648 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
649 // ---------------------------------------------------------------------
650 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
651 bool SimpleGenSources(CommandLine
&CmdL
)
653 if (CmdL
.FileSize() < 2)
654 return ShowHelp(CmdL
);
657 if (CmdL
.FileSize() >= 3)
658 Override
= CmdL
.FileList
[2];
661 if (Override
.empty() == false)
662 SOverride
= Override
+ ".src";
664 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
667 // Create a package writer object.
668 SourcesWriter
Sources(Override
,SOverride
);
669 if (_error
->PendingError() == true)
672 if (CmdL
.FileSize() >= 4)
673 Sources
.PathPrefix
= CmdL
.FileList
[3];
675 // Do recursive directory searching
676 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
682 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
683 // ---------------------------------------------------------------------
684 bool SimpleGenRelease(CommandLine
&CmdL
)
686 if (CmdL
.FileSize() < 2)
687 return ShowHelp(CmdL
);
689 string Dir
= CmdL
.FileList
[1];
691 ReleaseWriter
Release("");
692 Release
.DirStrip
= Dir
;
694 if (_error
->PendingError() == true)
697 if (Release
.RecursiveScan(Dir
) == false)
706 // Generate - Full generate, using a config file /*{{{*/
707 // ---------------------------------------------------------------------
709 bool Generate(CommandLine
&CmdL
)
711 struct CacheDB::Stats SrcStats
;
712 if (CmdL
.FileSize() < 2)
713 return ShowHelp(CmdL
);
715 struct timeval StartTime
;
716 gettimeofday(&StartTime
,0);
717 struct CacheDB::Stats Stats
;
719 // Read the configuration file.
721 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
724 vector
<PackageMap
> PkgList
;
725 LoadTree(PkgList
,Setup
);
726 LoadBinDir(PkgList
,Setup
);
728 // Sort by cache DB to improve IO locality.
729 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
732 if (CmdL
.FileSize() <= 2)
734 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
735 if (I
->GenPackages(Setup
,Stats
) == false)
736 _error
->DumpErrors();
737 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
738 if (I
->GenSources(Setup
,SrcStats
) == false)
739 _error
->DumpErrors();
743 // Make a choice list out of the package list..
744 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
745 RxChoiceList
*End
= List
;
746 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
748 End
->UserData
= &(*I
);
749 End
->Str
= I
->BaseDir
.c_str();
752 End
->UserData
= &(*I
);
753 End
->Str
= I
->Tag
.c_str();
759 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
762 return _error
->Error(_("No selections matched"));
764 _error
->DumpErrors();
766 // Do the generation for Packages
767 for (End
= List
; End
->Str
!= 0; End
++)
769 if (End
->Hit
== false)
772 PackageMap
*I
= (PackageMap
*)End
->UserData
;
773 if (I
->PkgDone
== true)
775 if (I
->GenPackages(Setup
,Stats
) == false)
776 _error
->DumpErrors();
779 // Do the generation for Sources
780 for (End
= List
; End
->Str
!= 0; End
++)
782 if (End
->Hit
== false)
785 PackageMap
*I
= (PackageMap
*)End
->UserData
;
786 if (I
->SrcDone
== true)
788 if (I
->GenSources(Setup
,SrcStats
) == false)
789 _error
->DumpErrors();
795 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
798 c1out
<< "Done Packages, Starting contents." << endl
;
800 // Sort the contents file list by date
801 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
802 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
805 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
806 I
->CntCompress
,A
) == false)
807 time(&I
->ContentsMTime
);
809 I
->ContentsMTime
= A
.st_mtime
;
811 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
813 /* Now for Contents.. The process here is to do a make-like dependency
814 check. Each contents file is verified to be newer than the package files
815 that describe the debs it indexes. Since the package files contain
816 hashes of the .debs this means they have not changed either so the
817 contents must be up to date. */
818 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
819 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
821 // This record is not relevent
822 if (I
->ContentsDone
== true ||
823 I
->Contents
.empty() == true)
826 // Do not do everything if the user specified sections.
827 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
831 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
833 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
835 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
839 if (A
.st_mtime
> B
.st_mtime
)
843 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
844 MaxContentsChange
) == false)
845 _error
->DumpErrors();
848 if (MaxContentsChange
== 0)
850 c1out
<< "Hit contents update byte limit" << endl
;
855 struct timeval NewTime
;
856 gettimeofday(&NewTime
,0);
857 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
858 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
859 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
860 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
865 // Clean - Clean out the databases /*{{{*/
866 // ---------------------------------------------------------------------
868 bool Clean(CommandLine
&CmdL
)
870 if (CmdL
.FileSize() != 2)
871 return ShowHelp(CmdL
);
873 // Read the configuration file.
875 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
878 vector
<PackageMap
> PkgList
;
879 LoadTree(PkgList
,Setup
);
880 LoadBinDir(PkgList
,Setup
);
882 // Sort by cache DB to improve IO locality.
883 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
885 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
887 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
889 c0out
<< I
->BinCacheDB
<< endl
;
890 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
891 if (DB
.Clean() == false)
892 _error
->DumpErrors();
894 string CacheDB
= I
->BinCacheDB
;
895 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; I
++);
902 int main(int argc
, const char *argv
[])
904 CommandLine::Args Args
[] = {
905 {'h',"help","help",0},
906 {0,"md5","APT::FTPArchive::MD5",0},
907 {'v',"version","version",0},
908 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
909 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
910 {'q',"quiet","quiet",CommandLine::IntLevel
},
911 {'q',"silent","quiet",CommandLine::IntLevel
},
912 {0,"delink","APT::FTPArchive::DeLinkAct",0},
913 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
914 {0,"contents","APT::FTPArchive::Contents",0},
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 (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;