]>
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>
28 #include "multicompress.h"
36 ofstream
devnull("/dev/null");
39 // struct PackageMap - List of all package files in the config file /*{{{*/
40 // ---------------------------------------------------------------------
46 string InternalPrefix
;
51 // Stuff for the Package File
57 // We generate for this given arch
60 // Stuff for the Source File
63 string SrcExtraOverride
;
75 unsigned int DeLinkLimit
;
83 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
85 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
86 {return x
.ContentsMTime
< y
.ContentsMTime
;};
89 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
91 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
92 {return x
.BinCacheDB
< y
.BinCacheDB
;};
95 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
96 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
97 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
98 bool GenContents(Configuration
&Setup
,
99 vector
<PackageMap
>::iterator Begin
,
100 vector
<PackageMap
>::iterator End
,
101 unsigned long &Left
);
103 PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
104 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
108 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
109 // ---------------------------------------------------------------------
111 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
113 PathPrefix
= Block
.Find("PathPrefix");
115 if (Block
.FindB("External-Links",true) == false)
116 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
120 PkgCompress
= Block
.Find("Packages::Compress",
121 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
122 CntCompress
= Block
.Find("Contents::Compress",
123 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
124 SrcCompress
= Block
.Find("Sources::Compress",
125 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
127 SrcExt
= Block
.Find("Sources::Extensions",
128 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
129 PkgExt
= Block
.Find("Packages::Extensions",
130 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
132 Permissions
= Setup
.FindI("Default::FileMode",0644);
134 if (FLFile
.empty() == false)
135 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
141 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
142 // ---------------------------------------------------------------------
143 /* This generates the Package File described by this object. */
144 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
146 if (PkgFile
.empty() == true)
149 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
150 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
151 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
153 struct timeval StartTime
;
154 gettimeofday(&StartTime
,0);
158 // Create a package writer object.
159 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
160 flCombine(OverrideDir
,BinOverride
),
161 flCombine(OverrideDir
,ExtraOverride
),
163 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
164 return _error
->Error(_("Package extension list is too long"));
165 if (_error
->PendingError() == true)
166 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
168 Packages
.PathPrefix
= PathPrefix
;
169 Packages
.DirStrip
= ArchiveDir
;
170 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
172 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
173 Packages
.DeLinkLimit
= DeLinkLimit
;
175 // Create a compressor object
176 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
177 PkgCompress
,Permissions
);
178 Packages
.Output
= Comp
.Input
;
179 if (_error
->PendingError() == true)
180 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
182 c0out
<< ' ' << BaseDir
<< ":" << flush
;
184 // Do recursive directory searching
185 if (FLFile
.empty() == true)
187 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
192 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
196 Packages
.Output
= 0; // Just in case
198 // Finish compressing
200 if (Comp
.Finalize(Size
) == false)
203 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
208 << SizeToStr(Size
) << "B ";
212 struct timeval NewTime
;
213 gettimeofday(&NewTime
,0);
214 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
215 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
217 c0out
<< Packages
.Stats
.Packages
<< " files " <<
218 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
219 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
220 TimeToStr((long)Delta
) << endl
;
222 Stats
.Add(Packages
.Stats
);
223 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
225 return !_error
->PendingError();
229 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
230 // ---------------------------------------------------------------------
231 /* This generates the Sources File described by this object. */
232 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
234 if (SrcFile
.empty() == true)
237 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
238 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
239 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
241 struct timeval StartTime
;
242 gettimeofday(&StartTime
,0);
246 // Create a package writer object.
247 SourcesWriter
Sources(flCombine(OverrideDir
,BinOverride
),
248 flCombine(OverrideDir
,SrcOverride
),
249 flCombine(OverrideDir
,SrcExtraOverride
));
250 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
251 return _error
->Error(_("Source extension list is too long"));
252 if (_error
->PendingError() == true)
253 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
255 Sources
.PathPrefix
= PathPrefix
;
256 Sources
.DirStrip
= ArchiveDir
;
257 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
259 Sources
.DeLinkLimit
= DeLinkLimit
;
260 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
262 // Create a compressor object
263 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
264 SrcCompress
,Permissions
);
265 Sources
.Output
= Comp
.Input
;
266 if (_error
->PendingError() == true)
267 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
269 c0out
<< ' ' << BaseDir
<< ":" << flush
;
271 // Do recursive directory searching
272 if (FLFile
.empty() == true)
274 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
279 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
282 Sources
.Output
= 0; // Just in case
284 // Finish compressing
286 if (Comp
.Finalize(Size
) == false)
289 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
294 << SizeToStr(Size
) << "B ";
298 struct timeval NewTime
;
299 gettimeofday(&NewTime
,0);
300 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
301 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
303 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
304 TimeToStr((long)Delta
) << endl
;
306 Stats
.Add(Sources
.Stats
);
307 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
309 return !_error
->PendingError();
312 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
313 // ---------------------------------------------------------------------
314 /* This generates the contents file partially described by this object.
315 It searches the given iterator range for other package files that map
316 into this contents file and includes their data as well when building. */
317 bool PackageMap::GenContents(Configuration
&Setup
,
318 vector
<PackageMap
>::iterator Begin
,
319 vector
<PackageMap
>::iterator End
,
322 if (Contents
.empty() == true)
328 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
329 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
330 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
332 struct timeval StartTime
;
333 gettimeofday(&StartTime
,0);
335 // Create a package writer object.
336 ContentsWriter
Contents("");
337 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
338 return _error
->Error(_("Package extension list is too long"));
339 if (_error
->PendingError() == true)
342 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
343 CntCompress
,Permissions
);
344 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
345 Contents
.Output
= Comp
.Input
;
346 if (_error
->PendingError() == true)
349 // Write the header out.
350 if (ContentsHead
.empty() == false)
352 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
353 if (_error
->PendingError() == true)
356 unsigned long Size
= Head
.Size();
357 unsigned char Buf
[4096];
360 unsigned long ToRead
= Size
;
361 if (Size
> sizeof(Buf
))
362 ToRead
= sizeof(Buf
);
364 if (Head
.Read(Buf
,ToRead
) == false)
367 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
368 return _error
->Errno("fwrite",_("Error writing header to contents file"));
374 /* Go over all the package file records and parse all the package
375 files associated with this contents file into one great big honking
376 memory structure, then dump the sorted version */
377 c0out
<< ' ' << this->Contents
<< ":" << flush
;
378 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; I
++)
380 if (I
->Contents
!= this->Contents
)
383 Contents
.Prefix
= ArchiveDir
;
384 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
385 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
388 I
->ContentsDone
= true;
393 // Finish compressing
395 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
398 return _error
->Error(_("Error processing contents %s"),
399 this->Contents
.c_str());
404 c0out
<< " New " << SizeToStr(Size
) << "B ";
413 struct timeval NewTime
;
414 gettimeofday(&NewTime
,0);
415 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
416 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
418 c0out
<< Contents
.Stats
.Packages
<< " files " <<
419 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
420 TimeToStr((long)Delta
) << endl
;
426 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
427 // ---------------------------------------------------------------------
428 /* This populates the PkgList with all the possible permutations of the
429 section/arch lists. */
430 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
433 string DDir
= Setup
.Find("TreeDefault::Directory",
434 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
435 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
436 "$(DIST)/$(SECTION)/source/");
437 string DPkg
= Setup
.Find("TreeDefault::Packages",
438 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
439 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
440 "$(DIST)/$(SECTION)/");
441 string DContents
= Setup
.Find("TreeDefault::Contents",
442 "$(DIST)/Contents-$(ARCH)");
443 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
444 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
445 "packages-$(ARCH).db");
446 string DSources
= Setup
.Find("TreeDefault::Sources",
447 "$(DIST)/$(SECTION)/source/Sources");
448 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
449 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
451 // Process 'tree' type sections
452 const Configuration::Item
*Top
= Setup
.Tree("tree");
453 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
455 Configuration
Block(Top
);
456 string Dist
= Top
->Tag
;
458 // Parse the sections
459 string Tmp
= Block
.Find("Sections");
460 const char *Sections
= Tmp
.c_str();
462 while (ParseQuoteWord(Sections
,Section
) == true)
464 string Tmp2
= Block
.Find("Architectures");
466 const char *Archs
= Tmp2
.c_str();
467 while (ParseQuoteWord(Archs
,Arch
) == true)
469 struct SubstVar Vars
[] = {{"$(DIST)",&Dist
},
470 {"$(SECTION)",&Section
},
475 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
476 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
478 if (stringcasecmp(Arch
,"source") == 0)
480 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
481 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
482 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
483 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
484 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
485 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
489 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
490 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
491 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
492 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
494 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
495 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
496 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
497 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
500 Itm
.GetGeneral(Setup
,Block
);
501 PkgList
.push_back(Itm
);
509 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
510 // ---------------------------------------------------------------------
512 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
514 // Process 'bindirectory' type sections
515 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
516 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
518 Configuration
Block(Top
);
521 Itm
.PkgFile
= Block
.Find("Packages");
522 Itm
.SrcFile
= Block
.Find("Sources");
523 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
524 Itm
.BinOverride
= Block
.Find("BinOverride");
525 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
526 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
527 Itm
.SrcOverride
= Block
.Find("SrcOverride");
528 Itm
.BaseDir
= Top
->Tag
;
529 Itm
.FLFile
= Block
.Find("FileList");
530 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
531 Itm
.Contents
= Block
.Find("Contents");
532 Itm
.ContentsHead
= Block
.Find("Contents::Header");
534 Itm
.GetGeneral(Setup
,Block
);
535 PkgList
.push_back(Itm
);
542 // ShowHelp - Show the help text /*{{{*/
543 // ---------------------------------------------------------------------
545 bool ShowHelp(CommandLine
&CmdL
)
547 ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,VERSION
,
548 COMMON_ARCH
,__DATE__
,__TIME__
);
549 if (_config
->FindB("version") == true)
553 _("Usage: apt-ftparchive [options] command\n"
554 "Commands: packages binarypath [overridefile [pathprefix]]\n"
555 " sources srcpath [overridefile [pathprefix]]\n"
558 " generate config [groups]\n"
561 "apt-ftparchive generates index files for Debian archives. It supports\n"
562 "many styles of generation from fully automated to functional replacements\n"
563 "for dpkg-scanpackages and dpkg-scansources\n"
565 "apt-ftparchive generates Package files from a tree of .debs. The\n"
566 "Package file contains the contents of all the control fields from\n"
567 "each package as well as the MD5 hash and filesize. An override file\n"
568 "is supported to force the value of Priority and Section.\n"
570 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
571 "The --source-override option can be used to specify a src override file\n"
573 "The 'packages' and 'sources' command should be run in the root of the\n"
574 "tree. BinaryPath should point to the base of the recursive search and \n"
575 "override file should contain the override flags. Pathprefix is\n"
576 "appended to the filename fields if present. Example usage from the \n"
578 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
579 " dists/potato/main/binary-i386/Packages\n"
582 " -h This help text\n"
583 " --md5 Control MD5 generation\n"
584 " -s=? Source override file\n"
586 " -d=? Select the optional caching database\n"
587 " --no-delink Enable delinking debug mode\n"
588 " --contents Control contents file generation\n"
589 " -c=? Read this configuration file\n"
590 " -o=? Set an arbitrary configuration option") << endl
;
595 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
596 // ---------------------------------------------------------------------
597 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
598 bool SimpleGenPackages(CommandLine
&CmdL
)
600 if (CmdL
.FileSize() < 2)
601 return ShowHelp(CmdL
);
604 if (CmdL
.FileSize() >= 3)
605 Override
= CmdL
.FileList
[2];
607 // Create a package writer object.
608 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
610 if (_error
->PendingError() == true)
613 if (CmdL
.FileSize() >= 4)
614 Packages
.PathPrefix
= CmdL
.FileList
[3];
616 // Do recursive directory searching
617 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
623 // SimpleGenContents - Generate a Contents listing /*{{{*/
624 // ---------------------------------------------------------------------
626 bool SimpleGenContents(CommandLine
&CmdL
)
628 if (CmdL
.FileSize() < 2)
629 return ShowHelp(CmdL
);
631 // Create a package writer object.
632 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"));
633 if (_error
->PendingError() == true)
636 // Do recursive directory searching
637 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
645 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
646 // ---------------------------------------------------------------------
647 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
648 bool SimpleGenSources(CommandLine
&CmdL
)
650 if (CmdL
.FileSize() < 2)
651 return ShowHelp(CmdL
);
654 if (CmdL
.FileSize() >= 3)
655 Override
= CmdL
.FileList
[2];
658 if (Override
.empty() == false)
659 SOverride
= Override
+ ".src";
661 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
664 // Create a package writer object.
665 SourcesWriter
Sources(Override
,SOverride
);
666 if (_error
->PendingError() == true)
669 if (CmdL
.FileSize() >= 4)
670 Sources
.PathPrefix
= CmdL
.FileList
[3];
672 // Do recursive directory searching
673 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
679 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
680 // ---------------------------------------------------------------------
681 bool SimpleGenRelease(CommandLine
&CmdL
)
683 if (CmdL
.FileSize() < 2)
684 return ShowHelp(CmdL
);
686 string Dir
= CmdL
.FileList
[1];
688 ReleaseWriter
Release("");
689 Release
.DirStrip
= Dir
;
691 if (_error
->PendingError() == true)
694 if (Release
.RecursiveScan(Dir
) == false)
703 // Generate - Full generate, using a config file /*{{{*/
704 // ---------------------------------------------------------------------
706 bool Generate(CommandLine
&CmdL
)
708 struct CacheDB::Stats SrcStats
;
709 if (CmdL
.FileSize() < 2)
710 return ShowHelp(CmdL
);
712 struct timeval StartTime
;
713 gettimeofday(&StartTime
,0);
714 struct CacheDB::Stats Stats
;
716 // Read the configuration file.
718 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
721 vector
<PackageMap
> PkgList
;
722 LoadTree(PkgList
,Setup
);
723 LoadBinDir(PkgList
,Setup
);
725 // Sort by cache DB to improve IO locality.
726 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
729 if (CmdL
.FileSize() <= 2)
731 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
732 if (I
->GenPackages(Setup
,Stats
) == false)
733 _error
->DumpErrors();
734 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
735 if (I
->GenSources(Setup
,SrcStats
) == false)
736 _error
->DumpErrors();
740 // Make a choice list out of the package list..
741 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
742 RxChoiceList
*End
= List
;
743 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
745 End
->UserData
= &(*I
);
746 End
->Str
= I
->BaseDir
.c_str();
749 End
->UserData
= &(*I
);
750 End
->Str
= I
->Tag
.c_str();
756 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
759 return _error
->Error(_("No selections matched"));
761 _error
->DumpErrors();
763 // Do the generation for Packages
764 for (End
= List
; End
->Str
!= 0; End
++)
766 if (End
->Hit
== false)
769 PackageMap
*I
= (PackageMap
*)End
->UserData
;
770 if (I
->PkgDone
== true)
772 if (I
->GenPackages(Setup
,Stats
) == false)
773 _error
->DumpErrors();
776 // Do the generation for Sources
777 for (End
= List
; End
->Str
!= 0; End
++)
779 if (End
->Hit
== false)
782 PackageMap
*I
= (PackageMap
*)End
->UserData
;
783 if (I
->SrcDone
== true)
785 if (I
->GenSources(Setup
,SrcStats
) == false)
786 _error
->DumpErrors();
792 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
795 c1out
<< "Packages done, Starting contents." << endl
;
797 // Sort the contents file list by date
798 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
799 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
802 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
803 I
->CntCompress
,A
) == false)
804 time(&I
->ContentsMTime
);
806 I
->ContentsMTime
= A
.st_mtime
;
808 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
810 /* Now for Contents.. The process here is to do a make-like dependency
811 check. Each contents file is verified to be newer than the package files
812 that describe the debs it indexes. Since the package files contain
813 hashes of the .debs this means they have not changed either so the
814 contents must be up to date. */
815 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
816 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
818 // This record is not relevent
819 if (I
->ContentsDone
== true ||
820 I
->Contents
.empty() == true)
823 // Do not do everything if the user specified sections.
824 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
828 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
830 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
832 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
836 if (A
.st_mtime
> B
.st_mtime
)
840 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
841 MaxContentsChange
) == false)
842 _error
->DumpErrors();
845 if (MaxContentsChange
== 0)
847 c1out
<< "Hit contents update byte limit" << endl
;
852 struct timeval NewTime
;
853 gettimeofday(&NewTime
,0);
854 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
855 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
856 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
857 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
862 // Clean - Clean out the databases /*{{{*/
863 // ---------------------------------------------------------------------
865 bool Clean(CommandLine
&CmdL
)
867 if (CmdL
.FileSize() != 2)
868 return ShowHelp(CmdL
);
870 // Read the configuration file.
872 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
875 vector
<PackageMap
> PkgList
;
876 LoadTree(PkgList
,Setup
);
877 LoadBinDir(PkgList
,Setup
);
879 // Sort by cache DB to improve IO locality.
880 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
882 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
884 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
886 c0out
<< I
->BinCacheDB
<< endl
;
887 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
888 if (DB
.Clean() == false)
889 _error
->DumpErrors();
891 string CacheDB
= I
->BinCacheDB
;
892 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; I
++);
899 int main(int argc
, const char *argv
[])
901 setlocale(LC_ALL
, "");
902 CommandLine::Args Args
[] = {
903 {'h',"help","help",0},
904 {0,"md5","APT::FTPArchive::MD5",0},
905 {'v',"version","version",0},
906 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
907 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
908 {'q',"quiet","quiet",CommandLine::IntLevel
},
909 {'q',"silent","quiet",CommandLine::IntLevel
},
910 {0,"delink","APT::FTPArchive::DeLinkAct",0},
911 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
912 {0,"contents","APT::FTPArchive::Contents",0},
913 {'c',"config-file",0,CommandLine::ConfigFile
},
914 {'o',"option",0,CommandLine::ArbItem
},
916 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
917 {"contents",&SimpleGenContents
},
918 {"sources",&SimpleGenSources
},
919 {"release",&SimpleGenRelease
},
920 {"generate",&Generate
},
925 // Parse the command line and initialize the package library
926 CommandLine
CmdL(Args
,_config
);
927 if (CmdL
.Parse(argc
,argv
) == false)
929 _error
->DumpErrors();
933 // See if the help should be shown
934 if (_config
->FindB("help") == true ||
935 _config
->FindB("version") == true ||
936 CmdL
.FileSize() == 0)
942 // Setup the output streams
943 c0out
.rdbuf(clog
.rdbuf());
944 c1out
.rdbuf(clog
.rdbuf());
945 c2out
.rdbuf(clog
.rdbuf());
946 Quiet
= _config
->FindI("quiet",0);
948 c0out
.rdbuf(devnull
.rdbuf());
950 c1out
.rdbuf(devnull
.rdbuf());
952 // Match the operation
953 CmdL
.DispatchArg(Cmds
);
955 if (_error
->empty() == false)
957 bool Errors
= _error
->PendingError();
958 _error
->DumpErrors();
959 return Errors
== true?100:0;