]>
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>
32 #include "multicompress.h"
40 ofstream
devnull("/dev/null");
43 // struct PackageMap - List of all package files in the config file /*{{{*/
44 // ---------------------------------------------------------------------
50 string InternalPrefix
;
55 // Stuff for the Package File
61 // We generate for this given arch
64 // Stuff for the Source File
67 string SrcExtraOverride
;
79 unsigned int DeLinkLimit
;
87 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
89 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
90 {return x
.ContentsMTime
< y
.ContentsMTime
;};
93 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
95 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
96 {return x
.BinCacheDB
< y
.BinCacheDB
;};
99 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
100 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
101 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
102 bool GenContents(Configuration
&Setup
,
103 vector
<PackageMap
>::iterator Begin
,
104 vector
<PackageMap
>::iterator End
,
105 unsigned long &Left
);
107 PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
108 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
112 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
113 // ---------------------------------------------------------------------
115 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
117 PathPrefix
= Block
.Find("PathPrefix");
119 if (Block
.FindB("External-Links",true) == false)
120 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
124 PkgCompress
= Block
.Find("Packages::Compress",
125 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
126 CntCompress
= Block
.Find("Contents::Compress",
127 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
128 SrcCompress
= Block
.Find("Sources::Compress",
129 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
131 SrcExt
= Block
.Find("Sources::Extensions",
132 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
133 PkgExt
= Block
.Find("Packages::Extensions",
134 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
136 Permissions
= Setup
.FindI("Default::FileMode",0644);
138 if (FLFile
.empty() == false)
139 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
145 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
146 // ---------------------------------------------------------------------
147 /* This generates the Package File described by this object. */
148 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
150 if (PkgFile
.empty() == true)
153 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
154 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
155 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
157 struct timeval StartTime
;
158 gettimeofday(&StartTime
,0);
162 // Create a package writer object.
163 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
164 flCombine(OverrideDir
,BinOverride
),
165 flCombine(OverrideDir
,ExtraOverride
),
167 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
168 return _error
->Error(_("Package extension list is too long"));
169 if (_error
->PendingError() == true)
170 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
172 Packages
.PathPrefix
= PathPrefix
;
173 Packages
.DirStrip
= ArchiveDir
;
174 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
176 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
177 Packages
.DeLinkLimit
= DeLinkLimit
;
179 // Create a compressor object
180 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
181 PkgCompress
,Permissions
);
182 Packages
.Output
= Comp
.Input
;
183 if (_error
->PendingError() == true)
184 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
186 c0out
<< ' ' << BaseDir
<< ":" << flush
;
188 // Do recursive directory searching
189 if (FLFile
.empty() == true)
191 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
196 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
200 Packages
.Output
= 0; // Just in case
202 // Finish compressing
204 if (Comp
.Finalize(Size
) == false)
207 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
212 << SizeToStr(Size
) << "B ";
216 struct timeval NewTime
;
217 gettimeofday(&NewTime
,0);
218 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
219 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
221 c0out
<< Packages
.Stats
.Packages
<< " files " <<
222 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
223 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
224 TimeToStr((long)Delta
) << endl
;
226 Stats
.Add(Packages
.Stats
);
227 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
229 return !_error
->PendingError();
233 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
234 // ---------------------------------------------------------------------
235 /* This generates the Sources File described by this object. */
236 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
238 if (SrcFile
.empty() == true)
241 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
242 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
243 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
245 struct timeval StartTime
;
246 gettimeofday(&StartTime
,0);
250 // Create a package writer object.
251 SourcesWriter
Sources(flCombine(OverrideDir
,BinOverride
),
252 flCombine(OverrideDir
,SrcOverride
),
253 flCombine(OverrideDir
,SrcExtraOverride
));
254 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
255 return _error
->Error(_("Source extension list is too long"));
256 if (_error
->PendingError() == true)
257 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
259 Sources
.PathPrefix
= PathPrefix
;
260 Sources
.DirStrip
= ArchiveDir
;
261 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
263 Sources
.DeLinkLimit
= DeLinkLimit
;
264 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
266 // Create a compressor object
267 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
268 SrcCompress
,Permissions
);
269 Sources
.Output
= Comp
.Input
;
270 if (_error
->PendingError() == true)
271 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
273 c0out
<< ' ' << BaseDir
<< ":" << flush
;
275 // Do recursive directory searching
276 if (FLFile
.empty() == true)
278 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
283 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
286 Sources
.Output
= 0; // Just in case
288 // Finish compressing
290 if (Comp
.Finalize(Size
) == false)
293 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
298 << SizeToStr(Size
) << "B ";
302 struct timeval NewTime
;
303 gettimeofday(&NewTime
,0);
304 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
305 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
307 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
308 TimeToStr((long)Delta
) << endl
;
310 Stats
.Add(Sources
.Stats
);
311 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
313 return !_error
->PendingError();
316 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
317 // ---------------------------------------------------------------------
318 /* This generates the contents file partially described by this object.
319 It searches the given iterator range for other package files that map
320 into this contents file and includes their data as well when building. */
321 bool PackageMap::GenContents(Configuration
&Setup
,
322 vector
<PackageMap
>::iterator Begin
,
323 vector
<PackageMap
>::iterator End
,
326 if (Contents
.empty() == true)
332 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
333 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
334 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
336 struct timeval StartTime
;
337 gettimeofday(&StartTime
,0);
339 // Create a package writer object.
340 ContentsWriter
Contents("");
341 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
342 return _error
->Error(_("Package extension list is too long"));
343 if (_error
->PendingError() == true)
346 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
347 CntCompress
,Permissions
);
348 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
349 Contents
.Output
= Comp
.Input
;
350 if (_error
->PendingError() == true)
353 // Write the header out.
354 if (ContentsHead
.empty() == false)
356 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
357 if (_error
->PendingError() == true)
360 unsigned long Size
= Head
.Size();
361 unsigned char Buf
[4096];
364 unsigned long ToRead
= Size
;
365 if (Size
> sizeof(Buf
))
366 ToRead
= sizeof(Buf
);
368 if (Head
.Read(Buf
,ToRead
) == false)
371 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
372 return _error
->Errno("fwrite",_("Error writing header to contents file"));
378 /* Go over all the package file records and parse all the package
379 files associated with this contents file into one great big honking
380 memory structure, then dump the sorted version */
381 c0out
<< ' ' << this->Contents
<< ":" << flush
;
382 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; I
++)
384 if (I
->Contents
!= this->Contents
)
387 Contents
.Prefix
= ArchiveDir
;
388 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
389 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
392 I
->ContentsDone
= true;
397 // Finish compressing
399 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
402 return _error
->Error(_("Error processing contents %s"),
403 this->Contents
.c_str());
408 c0out
<< " New " << SizeToStr(Size
) << "B ";
417 struct timeval NewTime
;
418 gettimeofday(&NewTime
,0);
419 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
420 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
422 c0out
<< Contents
.Stats
.Packages
<< " files " <<
423 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
424 TimeToStr((long)Delta
) << endl
;
430 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
431 // ---------------------------------------------------------------------
432 /* This populates the PkgList with all the possible permutations of the
433 section/arch lists. */
434 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
437 string DDir
= Setup
.Find("TreeDefault::Directory",
438 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
439 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
440 "$(DIST)/$(SECTION)/source/");
441 string DPkg
= Setup
.Find("TreeDefault::Packages",
442 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
443 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
444 "$(DIST)/$(SECTION)/");
445 string DContents
= Setup
.Find("TreeDefault::Contents",
446 "$(DIST)/Contents-$(ARCH)");
447 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
448 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
449 "packages-$(ARCH).db");
450 string DSources
= Setup
.Find("TreeDefault::Sources",
451 "$(DIST)/$(SECTION)/source/Sources");
452 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
453 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
455 // Process 'tree' type sections
456 const Configuration::Item
*Top
= Setup
.Tree("tree");
457 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
459 Configuration
Block(Top
);
460 string Dist
= Top
->Tag
;
462 // Parse the sections
463 string Tmp
= Block
.Find("Sections");
464 const char *Sections
= Tmp
.c_str();
466 while (ParseQuoteWord(Sections
,Section
) == true)
468 string Tmp2
= Block
.Find("Architectures");
470 const char *Archs
= Tmp2
.c_str();
471 while (ParseQuoteWord(Archs
,Arch
) == true)
473 struct SubstVar Vars
[] = {{"$(DIST)",&Dist
},
474 {"$(SECTION)",&Section
},
479 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
480 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
482 if (stringcasecmp(Arch
,"source") == 0)
484 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
485 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
486 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
487 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
488 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
489 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
493 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
494 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
495 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
496 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
498 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
499 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
500 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
501 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
504 Itm
.GetGeneral(Setup
,Block
);
505 PkgList
.push_back(Itm
);
513 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
514 // ---------------------------------------------------------------------
516 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
518 // Process 'bindirectory' type sections
519 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
520 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
522 Configuration
Block(Top
);
525 Itm
.PkgFile
= Block
.Find("Packages");
526 Itm
.SrcFile
= Block
.Find("Sources");
527 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
528 Itm
.BinOverride
= Block
.Find("BinOverride");
529 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
530 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
531 Itm
.SrcOverride
= Block
.Find("SrcOverride");
532 Itm
.BaseDir
= Top
->Tag
;
533 Itm
.FLFile
= Block
.Find("FileList");
534 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
535 Itm
.Contents
= Block
.Find("Contents");
536 Itm
.ContentsHead
= Block
.Find("Contents::Header");
538 Itm
.GetGeneral(Setup
,Block
);
539 PkgList
.push_back(Itm
);
546 // ShowHelp - Show the help text /*{{{*/
547 // ---------------------------------------------------------------------
549 bool ShowHelp(CommandLine
&CmdL
)
551 ioprintf(cout
,_("%s %s for %s %s compiled on %s %s\n"),PACKAGE
,VERSION
,
552 COMMON_OS
,COMMON_CPU
,__DATE__
,__TIME__
);
553 if (_config
->FindB("version") == true)
557 _("Usage: apt-ftparchive [options] command\n"
558 "Commands: packages binarypath [overridefile [pathprefix]]\n"
559 " sources srcpath [overridefile [pathprefix]]\n"
562 " generate config [groups]\n"
565 "apt-ftparchive generates index files for Debian archives. It supports\n"
566 "many styles of generation from fully automated to functional replacements\n"
567 "for dpkg-scanpackages and dpkg-scansources\n"
569 "apt-ftparchive generates Package files from a tree of .debs. The\n"
570 "Package file contains the contents of all the control fields from\n"
571 "each package as well as the MD5 hash and filesize. An override file\n"
572 "is supported to force the value of Priority and Section.\n"
574 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
575 "The --source-override option can be used to specify a src override file\n"
577 "The 'packages' and 'sources' command should be run in the root of the\n"
578 "tree. BinaryPath should point to the base of the recursive search and \n"
579 "override file should contain the override flags. Pathprefix is\n"
580 "appended to the filename fields if present. Example usage from the \n"
582 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
583 " dists/potato/main/binary-i386/Packages\n"
586 " -h This help text\n"
587 " --md5 Control MD5 generation\n"
588 " -s=? Source override file\n"
590 " -d=? Select the optional caching database\n"
591 " --no-delink Enable delinking debug mode\n"
592 " --contents Control contents file generation\n"
593 " -c=? Read this configuration file\n"
594 " -o=? Set an arbitrary configuration option") << endl
;
599 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
600 // ---------------------------------------------------------------------
601 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
602 bool SimpleGenPackages(CommandLine
&CmdL
)
604 if (CmdL
.FileSize() < 2)
605 return ShowHelp(CmdL
);
608 if (CmdL
.FileSize() >= 3)
609 Override
= CmdL
.FileList
[2];
611 // Create a package writer object.
612 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
614 if (_error
->PendingError() == true)
617 if (CmdL
.FileSize() >= 4)
618 Packages
.PathPrefix
= CmdL
.FileList
[3];
620 // Do recursive directory searching
621 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
627 // SimpleGenContents - Generate a Contents listing /*{{{*/
628 // ---------------------------------------------------------------------
630 bool SimpleGenContents(CommandLine
&CmdL
)
632 if (CmdL
.FileSize() < 2)
633 return ShowHelp(CmdL
);
635 // Create a package writer object.
636 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"));
637 if (_error
->PendingError() == true)
640 // Do recursive directory searching
641 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
649 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
650 // ---------------------------------------------------------------------
651 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
652 bool SimpleGenSources(CommandLine
&CmdL
)
654 if (CmdL
.FileSize() < 2)
655 return ShowHelp(CmdL
);
658 if (CmdL
.FileSize() >= 3)
659 Override
= CmdL
.FileList
[2];
662 if (Override
.empty() == false)
663 SOverride
= Override
+ ".src";
665 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
668 // Create a package writer object.
669 SourcesWriter
Sources(Override
,SOverride
);
670 if (_error
->PendingError() == true)
673 if (CmdL
.FileSize() >= 4)
674 Sources
.PathPrefix
= CmdL
.FileList
[3];
676 // Do recursive directory searching
677 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
683 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
684 // ---------------------------------------------------------------------
685 bool SimpleGenRelease(CommandLine
&CmdL
)
687 if (CmdL
.FileSize() < 2)
688 return ShowHelp(CmdL
);
690 string Dir
= CmdL
.FileList
[1];
692 ReleaseWriter
Release("");
693 Release
.DirStrip
= Dir
;
695 if (_error
->PendingError() == true)
698 if (Release
.RecursiveScan(Dir
) == false)
707 // Generate - Full generate, using a config file /*{{{*/
708 // ---------------------------------------------------------------------
710 bool Generate(CommandLine
&CmdL
)
712 struct CacheDB::Stats SrcStats
;
713 if (CmdL
.FileSize() < 2)
714 return ShowHelp(CmdL
);
716 struct timeval StartTime
;
717 gettimeofday(&StartTime
,0);
718 struct CacheDB::Stats Stats
;
720 // Read the configuration file.
722 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
725 vector
<PackageMap
> PkgList
;
726 LoadTree(PkgList
,Setup
);
727 LoadBinDir(PkgList
,Setup
);
729 // Sort by cache DB to improve IO locality.
730 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
733 if (CmdL
.FileSize() <= 2)
735 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
736 if (I
->GenPackages(Setup
,Stats
) == false)
737 _error
->DumpErrors();
738 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
739 if (I
->GenSources(Setup
,SrcStats
) == false)
740 _error
->DumpErrors();
744 // Make a choice list out of the package list..
745 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
746 RxChoiceList
*End
= List
;
747 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
749 End
->UserData
= &(*I
);
750 End
->Str
= I
->BaseDir
.c_str();
753 End
->UserData
= &(*I
);
754 End
->Str
= I
->Tag
.c_str();
760 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
763 return _error
->Error(_("No selections matched"));
765 _error
->DumpErrors();
767 // Do the generation for Packages
768 for (End
= List
; End
->Str
!= 0; End
++)
770 if (End
->Hit
== false)
773 PackageMap
*I
= (PackageMap
*)End
->UserData
;
774 if (I
->PkgDone
== true)
776 if (I
->GenPackages(Setup
,Stats
) == false)
777 _error
->DumpErrors();
780 // Do the generation for Sources
781 for (End
= List
; End
->Str
!= 0; End
++)
783 if (End
->Hit
== false)
786 PackageMap
*I
= (PackageMap
*)End
->UserData
;
787 if (I
->SrcDone
== true)
789 if (I
->GenSources(Setup
,SrcStats
) == false)
790 _error
->DumpErrors();
796 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
799 c1out
<< "Done Packages, Starting contents." << endl
;
801 // Sort the contents file list by date
802 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
803 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
806 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
807 I
->CntCompress
,A
) == false)
808 time(&I
->ContentsMTime
);
810 I
->ContentsMTime
= A
.st_mtime
;
812 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
814 /* Now for Contents.. The process here is to do a make-like dependency
815 check. Each contents file is verified to be newer than the package files
816 that describe the debs it indexes. Since the package files contain
817 hashes of the .debs this means they have not changed either so the
818 contents must be up to date. */
819 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
820 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
822 // This record is not relevent
823 if (I
->ContentsDone
== true ||
824 I
->Contents
.empty() == true)
827 // Do not do everything if the user specified sections.
828 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
832 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
834 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
836 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
840 if (A
.st_mtime
> B
.st_mtime
)
844 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
845 MaxContentsChange
) == false)
846 _error
->DumpErrors();
849 if (MaxContentsChange
== 0)
851 c1out
<< "Hit contents update byte limit" << endl
;
856 struct timeval NewTime
;
857 gettimeofday(&NewTime
,0);
858 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
859 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
860 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
861 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
866 // Clean - Clean out the databases /*{{{*/
867 // ---------------------------------------------------------------------
869 bool Clean(CommandLine
&CmdL
)
871 if (CmdL
.FileSize() != 2)
872 return ShowHelp(CmdL
);
874 // Read the configuration file.
876 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
879 vector
<PackageMap
> PkgList
;
880 LoadTree(PkgList
,Setup
);
881 LoadBinDir(PkgList
,Setup
);
883 // Sort by cache DB to improve IO locality.
884 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
886 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
888 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
890 c0out
<< I
->BinCacheDB
<< endl
;
891 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
892 if (DB
.Clean() == false)
893 _error
->DumpErrors();
895 string CacheDB
= I
->BinCacheDB
;
896 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; I
++);
903 int main(int argc
, const char *argv
[])
905 CommandLine::Args Args
[] = {
906 {'h',"help","help",0},
907 {0,"md5","APT::FTPArchive::MD5",0},
908 {'v',"version","version",0},
909 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
910 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
911 {'q',"quiet","quiet",CommandLine::IntLevel
},
912 {'q',"silent","quiet",CommandLine::IntLevel
},
913 {0,"delink","APT::FTPArchive::DeLinkAct",0},
914 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
915 {0,"contents","APT::FTPArchive::Contents",0},
916 {'c',"config-file",0,CommandLine::ConfigFile
},
917 {'o',"option",0,CommandLine::ArbItem
},
919 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
920 {"contents",&SimpleGenContents
},
921 {"sources",&SimpleGenSources
},
922 {"release",&SimpleGenRelease
},
923 {"generate",&Generate
},
928 // Parse the command line and initialize the package library
929 CommandLine
CmdL(Args
,_config
);
930 if (CmdL
.Parse(argc
,argv
) == false)
932 _error
->DumpErrors();
936 // See if the help should be shown
937 if (_config
->FindB("help") == true ||
938 _config
->FindB("version") == true ||
939 CmdL
.FileSize() == 0)
945 // Setup the output streams
946 c0out
.rdbuf(clog
.rdbuf());
947 c1out
.rdbuf(clog
.rdbuf());
948 c2out
.rdbuf(clog
.rdbuf());
949 Quiet
= _config
->FindI("quiet",0);
951 c0out
.rdbuf(devnull
.rdbuf());
953 c1out
.rdbuf(devnull
.rdbuf());
955 // Match the operation
956 CmdL
.DispatchArg(Cmds
);
958 if (_error
->empty() == false)
960 bool Errors
= _error
->PendingError();
961 _error
->DumpErrors();
962 return Errors
== true?100:0;