]>
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 /*{{{*/
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>
20 #include <apt-pkg/fileutl.h>
22 #include <apt-private/private-cmndline.h>
23 #include <apt-private/private-output.h>
39 #include "apt-ftparchive.h"
40 #include "multicompress.h"
49 // struct PackageMap - List of all package files in the config file /*{{{*/
50 // ---------------------------------------------------------------------
56 string InternalPrefix
;
61 // Stuff for the Package File
68 // We generate for this given arch
71 // Stuff for the Source File
74 string SrcExtraOverride
;
76 // Translation master file
78 TranslationWriter
*TransWriter
;
90 unsigned int DeLinkLimit
;
98 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
100 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
101 {return x
.ContentsMTime
< y
.ContentsMTime
;};
104 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
106 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
107 {return x
.BinCacheDB
< y
.BinCacheDB
;};
110 struct SrcDBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
112 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
113 {return x
.SrcCacheDB
< y
.SrcCacheDB
;};
116 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
117 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
118 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
119 bool GenContents(Configuration
&Setup
,
120 vector
<PackageMap
>::iterator Begin
,
121 vector
<PackageMap
>::iterator End
,
122 unsigned long &Left
);
124 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
125 ContentsDone(false), PkgDone(false), SrcDone(false),
130 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
131 // ---------------------------------------------------------------------
133 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
135 PathPrefix
= Block
.Find("PathPrefix");
137 if (Block
.FindB("External-Links",true) == false)
138 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit", std::numeric_limits
<unsigned int>::max());
142 PkgCompress
= Block
.Find("Packages::Compress",
143 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
144 CntCompress
= Block
.Find("Contents::Compress",
145 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
146 SrcCompress
= Block
.Find("Sources::Compress",
147 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
149 SrcExt
= Block
.Find("Sources::Extensions",
150 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
151 PkgExt
= Block
.Find("Packages::Extensions",
152 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
154 Permissions
= Setup
.FindI("Default::FileMode",0644);
156 if (FLFile
.empty() == false)
157 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
163 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
164 // ---------------------------------------------------------------------
165 /* This generates the Package File described by this object. */
166 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
168 if (PkgFile
.empty() == true)
171 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
172 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
173 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
175 struct timeval StartTime
;
176 gettimeofday(&StartTime
,0);
180 // Create a package writer object.
181 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
182 PkgCompress
,Permissions
);
183 PackagesWriter
Packages(&Comp
.Input
, TransWriter
, flCombine(CacheDir
,BinCacheDB
),
184 flCombine(OverrideDir
,BinOverride
),
185 flCombine(OverrideDir
,ExtraOverride
),
187 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
188 return _error
->Error(_("Package extension list is too long"));
189 if (_error
->PendingError() == true)
190 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
192 Packages
.PathPrefix
= PathPrefix
;
193 Packages
.DirStrip
= ArchiveDir
;
194 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
196 Packages
.LongDescription
= LongDesc
;
198 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
199 Packages
.DeLinkLimit
= DeLinkLimit
;
201 if (_error
->PendingError() == true)
202 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
204 c0out
<< ' ' << BaseDir
<< ":" << flush
;
206 // Do recursive directory searching
207 if (FLFile
.empty() == true)
209 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
214 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
218 Packages
.Output
= 0; // Just in case
220 // Finish compressing
221 unsigned long long Size
;
222 if (Comp
.Finalize(Size
) == false)
225 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
230 << SizeToStr(Size
) << "B ";
234 struct timeval NewTime
;
235 gettimeofday(&NewTime
,0);
236 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
237 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
239 c0out
<< Packages
.Stats
.Packages
<< " files " <<
240 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
241 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
242 TimeToStr((long)Delta
) << endl
;
244 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
245 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
247 Stats
.Add(Packages
.Stats
);
248 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
250 return !_error
->PendingError();
254 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
255 // ---------------------------------------------------------------------
256 /* This generates the Sources File described by this object. */
257 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
259 if (SrcFile
.empty() == true)
262 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
263 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
264 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
266 struct timeval StartTime
;
267 gettimeofday(&StartTime
,0);
271 // Create a package writer object.
272 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
273 SrcCompress
,Permissions
);
274 SourcesWriter
Sources(&Comp
.Input
, flCombine(CacheDir
, SrcCacheDB
),
275 flCombine(OverrideDir
,BinOverride
),
276 flCombine(OverrideDir
,SrcOverride
),
277 flCombine(OverrideDir
,SrcExtraOverride
));
278 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
279 return _error
->Error(_("Source extension list is too long"));
280 if (_error
->PendingError() == true)
281 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
283 Sources
.PathPrefix
= PathPrefix
;
284 Sources
.DirStrip
= ArchiveDir
;
285 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
287 Sources
.DeLinkLimit
= DeLinkLimit
;
288 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
290 if (_error
->PendingError() == true)
291 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
293 c0out
<< ' ' << BaseDir
<< ":" << flush
;
295 // Do recursive directory searching
296 if (FLFile
.empty() == true)
298 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
303 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
306 Sources
.Output
= 0; // Just in case
308 // Finish compressing
309 unsigned long long Size
;
310 if (Comp
.Finalize(Size
) == false)
313 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
318 << SizeToStr(Size
) << "B ";
322 struct timeval NewTime
;
323 gettimeofday(&NewTime
,0);
324 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
325 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
327 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
328 TimeToStr((long)Delta
) << endl
;
330 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
331 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
333 Stats
.Add(Sources
.Stats
);
334 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
336 return !_error
->PendingError();
339 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
340 // ---------------------------------------------------------------------
341 /* This generates the contents file partially described by this object.
342 It searches the given iterator range for other package files that map
343 into this contents file and includes their data as well when building. */
344 bool PackageMap::GenContents(Configuration
&Setup
,
345 vector
<PackageMap
>::iterator Begin
,
346 vector
<PackageMap
>::iterator End
,
349 if (Contents
.empty() == true)
355 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
356 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
357 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
359 struct timeval StartTime
;
360 gettimeofday(&StartTime
,0);
362 // Create a package writer object.
363 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
364 CntCompress
,Permissions
);
365 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
366 ContentsWriter
Contents(&Comp
.Input
, "", Arch
);
367 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
368 return _error
->Error(_("Package extension list is too long"));
369 if (_error
->PendingError() == true)
372 if (_error
->PendingError() == true)
375 // Write the header out.
376 if (ContentsHead
.empty() == false)
378 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
379 if (_error
->PendingError() == true)
382 unsigned long long Size
= Head
.Size();
383 unsigned char Buf
[4096];
386 unsigned long long ToRead
= Size
;
387 if (Size
> sizeof(Buf
))
388 ToRead
= sizeof(Buf
);
390 if (Head
.Read(Buf
,ToRead
) == false)
393 if (Comp
.Input
.Write(Buf
, ToRead
) == false)
394 return _error
->Errno("fwrite",_("Error writing header to contents file"));
400 /* Go over all the package file records and parse all the package
401 files associated with this contents file into one great big honking
402 memory structure, then dump the sorted version */
403 c0out
<< ' ' << this->Contents
<< ":" << flush
;
404 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
406 if (I
->Contents
!= this->Contents
)
409 Contents
.Prefix
= ArchiveDir
;
410 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
411 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
414 I
->ContentsDone
= true;
419 // Finish compressing
420 unsigned long long Size
;
421 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
424 return _error
->Error(_("Error processing contents %s"),
425 this->Contents
.c_str());
430 c0out
<< " New " << SizeToStr(Size
) << "B ";
439 struct timeval NewTime
;
440 gettimeofday(&NewTime
,0);
441 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
442 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
444 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
445 c0out
<< " Misses in Cache: " << Contents
.Stats
.Misses
<< endl
;
447 c0out
<< Contents
.Stats
.Packages
<< " files " <<
448 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
449 TimeToStr((long)Delta
) << endl
;
455 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
456 // ---------------------------------------------------------------------
457 /* This populates the PkgList with all the possible permutations of the
458 section/arch lists. */
459 static void LoadTree(vector
<PackageMap
> &PkgList
, std::vector
<TranslationWriter
*> &TransList
, Configuration
&Setup
)
462 string DDir
= Setup
.Find("TreeDefault::Directory",
463 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
464 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
465 "$(DIST)/$(SECTION)/source/");
466 string DPkg
= Setup
.Find("TreeDefault::Packages",
467 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
468 string DTrans
= Setup
.Find("TreeDefault::Translation",
469 "$(DIST)/$(SECTION)/i18n/Translation-en");
470 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
471 "$(DIST)/$(SECTION)/");
472 string DContents
= Setup
.Find("TreeDefault::Contents",
473 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
474 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
475 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
476 "packages-$(ARCH).db");
477 string SrcDBCache
= Setup
.Find("TreeDefault::SrcCacheDB",
478 "sources-$(SECTION).db");
479 string DSources
= Setup
.Find("TreeDefault::Sources",
480 "$(DIST)/$(SECTION)/source/Sources");
481 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
482 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
484 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
486 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
487 _config
->FindB("APT::FTPArchive::LongDescription", true));
488 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
490 // Process 'tree' type sections
491 const Configuration::Item
*Top
= Setup
.Tree("tree");
492 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
494 Configuration
Block(Top
);
495 string Dist
= Top
->Tag
;
497 // Parse the sections
498 string Tmp
= Block
.Find("Sections");
499 const char *Sections
= Tmp
.c_str();
501 while (ParseQuoteWord(Sections
,Section
) == true)
504 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
505 {"$(SECTION)",&Section
},
508 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
509 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
510 TranslationWriter
*TransWriter
= NULL
;
512 string
const Tmp2
= Block
.Find("Architectures");
513 const char *Archs
= Tmp2
.c_str();
514 while (ParseQuoteWord(Archs
,Arch
) == true)
517 Itm
.Permissions
= Perms
;
518 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
519 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
521 if (stringcasecmp(Arch
,"source") == 0)
523 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
524 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
525 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
526 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
527 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
528 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
529 Itm
.SrcCacheDB
= SubstVar(Block
.Find("SrcCacheDB",SrcDBCache
.c_str()),Vars
);
533 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
534 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
535 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
536 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
538 Itm
.LongDesc
= LongDesc
;
539 if (TransWriter
== NULL
&& DTrans
.empty() == false && LongDesc
== false && DTrans
!= "/dev/null")
541 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
542 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
543 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
544 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
545 TransList
.push_back(TransWriter
);
547 Itm
.TransWriter
= TransWriter
;
548 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
549 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
550 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
551 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
554 Itm
.GetGeneral(Setup
,Block
);
555 PkgList
.push_back(Itm
);
563 static void UnloadTree(std::vector
<TranslationWriter
*> const &Trans
) /*{{{*/
565 for (std::vector
<TranslationWriter
*>::const_reverse_iterator T
= Trans
.rbegin(); T
!= Trans
.rend(); ++T
)
569 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
570 // ---------------------------------------------------------------------
572 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
574 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
576 // Process 'bindirectory' type sections
577 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
578 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
580 Configuration
Block(Top
);
583 Itm
.PkgFile
= Block
.Find("Packages");
584 Itm
.SrcFile
= Block
.Find("Sources");
585 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
586 Itm
.SrcCacheDB
= Block
.Find("SrcCacheDB");
587 Itm
.BinOverride
= Block
.Find("BinOverride");
588 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
589 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
590 Itm
.SrcOverride
= Block
.Find("SrcOverride");
591 Itm
.BaseDir
= Top
->Tag
;
592 Itm
.FLFile
= Block
.Find("FileList");
593 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
594 Itm
.Contents
= Block
.Find("Contents");
595 Itm
.ContentsHead
= Block
.Find("Contents::Header");
596 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
598 Itm
.GetGeneral(Setup
,Block
);
599 PkgList
.push_back(Itm
);
606 // ShowHelp - Show the help text /*{{{*/
607 // ---------------------------------------------------------------------
609 static bool ShowHelp(CommandLine
&)
611 ioprintf(cout
, "%s %s (%s)\n", PACKAGE
, PACKAGE_VERSION
, COMMON_ARCH
);
612 if (_config
->FindB("version") == true)
616 _("Usage: apt-ftparchive [options] command\n"
617 "Commands: packages binarypath [overridefile [pathprefix]]\n"
618 " sources srcpath [overridefile [pathprefix]]\n"
621 " generate config [groups]\n"
624 "apt-ftparchive generates index files for Debian archives. It supports\n"
625 "many styles of generation from fully automated to functional replacements\n"
626 "for dpkg-scanpackages and dpkg-scansources\n"
628 "apt-ftparchive generates Package files from a tree of .debs. The\n"
629 "Package file contains the contents of all the control fields from\n"
630 "each package as well as the MD5 hash and filesize. An override file\n"
631 "is supported to force the value of Priority and Section.\n"
633 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
634 "The --source-override option can be used to specify a src override file\n"
636 "The 'packages' and 'sources' command should be run in the root of the\n"
637 "tree. BinaryPath should point to the base of the recursive search and \n"
638 "override file should contain the override flags. Pathprefix is\n"
639 "appended to the filename fields if present. Example usage from the \n"
641 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
642 " dists/potato/main/binary-i386/Packages\n"
645 " -h This help text\n"
646 " --md5 Control MD5 generation\n"
647 " -s=? Source override file\n"
649 " -d=? Select the optional caching database\n"
650 " --no-delink Enable delinking debug mode\n"
651 " --contents Control contents file generation\n"
652 " -c=? Read this configuration file\n"
653 " -o=? Set an arbitrary configuration option") << endl
;
658 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
659 // ---------------------------------------------------------------------
660 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
661 static bool SimpleGenPackages(CommandLine
&CmdL
)
663 if (CmdL
.FileSize() < 2)
664 return ShowHelp(CmdL
);
667 if (CmdL
.FileSize() >= 3)
668 Override
= CmdL
.FileList
[2];
670 // Create a package writer object.
671 PackagesWriter
Packages(NULL
, NULL
, _config
->Find("APT::FTPArchive::DB"),
672 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
673 if (_error
->PendingError() == true)
676 if (CmdL
.FileSize() >= 4)
677 Packages
.PathPrefix
= CmdL
.FileList
[3];
679 // Do recursive directory searching
680 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
683 // Give some stats if asked for
684 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
685 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
690 // SimpleGenContents - Generate a Contents listing /*{{{*/
691 // ---------------------------------------------------------------------
693 static bool SimpleGenContents(CommandLine
&CmdL
)
695 if (CmdL
.FileSize() < 2)
696 return ShowHelp(CmdL
);
698 // Create a package writer object.
699 ContentsWriter
Contents(NULL
, _config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
700 if (_error
->PendingError() == true)
703 // Do recursive directory searching
704 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
712 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
713 // ---------------------------------------------------------------------
714 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
715 static bool SimpleGenSources(CommandLine
&CmdL
)
717 if (CmdL
.FileSize() < 2)
718 return ShowHelp(CmdL
);
721 if (CmdL
.FileSize() >= 3)
722 Override
= CmdL
.FileList
[2];
725 if (Override
.empty() == false)
726 SOverride
= Override
+ ".src";
728 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
731 // Create a package writer object.
732 SourcesWriter
Sources(NULL
, _config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
733 if (_error
->PendingError() == true)
736 if (CmdL
.FileSize() >= 4)
737 Sources
.PathPrefix
= CmdL
.FileList
[3];
739 // Do recursive directory searching
740 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
743 // Give some stats if asked for
744 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
745 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
750 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
751 // ---------------------------------------------------------------------
752 static bool SimpleGenRelease(CommandLine
&CmdL
)
754 if (CmdL
.FileSize() < 2)
755 return ShowHelp(CmdL
);
757 string Dir
= CmdL
.FileList
[1];
759 ReleaseWriter
Release(NULL
, "");
760 Release
.DirStrip
= Dir
;
762 if (_error
->PendingError() == true)
765 if (Release
.RecursiveScan(Dir
) == false)
774 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
775 // ---------------------------------------------------------------------
776 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
777 vector
<PackageMap
> &PkgList
,
778 struct CacheDB::Stats
&SrcStats
,
779 struct CacheDB::Stats
&Stats
,
782 if (CmdL
.FileSize() <= 2)
784 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
785 if (I
->GenPackages(Setup
,Stats
) == false)
786 _error
->DumpErrors();
787 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
788 if (I
->GenSources(Setup
,SrcStats
) == false)
789 _error
->DumpErrors();
793 // Make a choice list out of the package list..
794 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
795 RxChoiceList
*End
= List
;
796 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
798 End
->UserData
= &(*I
);
799 End
->Str
= I
->BaseDir
.c_str();
802 End
->UserData
= &(*I
);
803 End
->Str
= I
->Tag
.c_str();
809 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
812 return _error
->Error(_("No selections matched"));
814 _error
->DumpErrors();
816 // Do the generation for Packages
817 for (End
= List
; End
->Str
!= 0; End
++)
819 if (End
->Hit
== false)
822 PackageMap
*I
= (PackageMap
*)End
->UserData
;
823 if (I
->PkgDone
== true)
825 if (I
->GenPackages(Setup
,Stats
) == false)
826 _error
->DumpErrors();
829 // Do the generation for Sources
830 for (End
= List
; End
->Str
!= 0; End
++)
832 if (End
->Hit
== false)
835 PackageMap
*I
= (PackageMap
*)End
->UserData
;
836 if (I
->SrcDone
== true)
838 if (I
->GenSources(Setup
,SrcStats
) == false)
839 _error
->DumpErrors();
848 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
849 // ---------------------------------------------------------------------
850 static bool DoGenerateContents(Configuration
&Setup
,
851 vector
<PackageMap
> &PkgList
,
854 c1out
<< "Packages done, Starting contents." << endl
;
856 // Sort the contents file list by date
857 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
858 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
861 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
862 I
->CntCompress
,A
) == false)
863 time(&I
->ContentsMTime
);
865 I
->ContentsMTime
= A
.st_mtime
;
867 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
869 /* Now for Contents.. The process here is to do a make-like dependency
870 check. Each contents file is verified to be newer than the package files
871 that describe the debs it indexes. Since the package files contain
872 hashes of the .debs this means they have not changed either so the
873 contents must be up to date. */
874 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",
875 std::numeric_limits
<unsigned int>::max())*1024;
876 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
878 // This record is not relevant
879 if (I
->ContentsDone
== true ||
880 I
->Contents
.empty() == true)
883 // Do not do everything if the user specified sections.
884 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
888 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
890 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
892 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
896 if (A
.st_mtime
> B
.st_mtime
)
900 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
901 MaxContentsChange
) == false)
902 _error
->DumpErrors();
905 if (MaxContentsChange
== 0)
907 c1out
<< "Hit contents update byte limit" << endl
;
916 // Generate - Full generate, using a config file /*{{{*/
917 // ---------------------------------------------------------------------
919 static bool Generate(CommandLine
&CmdL
)
921 struct CacheDB::Stats SrcStats
;
922 if (CmdL
.FileSize() < 2)
923 return ShowHelp(CmdL
);
925 struct timeval StartTime
;
926 gettimeofday(&StartTime
,0);
927 struct CacheDB::Stats Stats
;
929 // Read the configuration file.
931 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
934 vector
<PackageMap
> PkgList
;
935 std::vector
<TranslationWriter
*> TransList
;
936 LoadTree(PkgList
, TransList
, Setup
);
937 LoadBinDir(PkgList
,Setup
);
939 // Sort by cache DB to improve IO locality.
940 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
941 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
944 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
946 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
948 UnloadTree(TransList
);
952 c1out
<< "Skipping Packages/Sources generation" << endl
;
955 // do Contents if needed
956 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
957 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
959 UnloadTree(TransList
);
963 struct timeval NewTime
;
964 gettimeofday(&NewTime
,0);
965 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
966 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
967 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
968 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
970 UnloadTree(TransList
);
975 // Clean - Clean out the databases /*{{{*/
976 // ---------------------------------------------------------------------
978 static bool Clean(CommandLine
&CmdL
)
980 if (CmdL
.FileSize() != 2)
981 return ShowHelp(CmdL
);
983 // Read the configuration file.
985 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
987 // we don't need translation creation here
988 Setup
.Set("TreeDefault::Translation", "/dev/null");
990 vector
<PackageMap
> PkgList
;
991 std::vector
<TranslationWriter
*> TransList
;
992 LoadTree(PkgList
, TransList
, Setup
);
993 LoadBinDir(PkgList
,Setup
);
995 // Sort by cache DB to improve IO locality.
996 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
997 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
999 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1001 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1003 if(I
->BinCacheDB
!= "")
1004 c0out
<< I
->BinCacheDB
<< endl
;
1005 if(I
->SrcCacheDB
!= "")
1006 c0out
<< I
->SrcCacheDB
<< endl
;
1007 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1008 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1009 if (DB
.Clean() == false)
1010 _error
->DumpErrors();
1011 if (DB_SRC
.Clean() == false)
1012 _error
->DumpErrors();
1014 string CacheDB
= I
->BinCacheDB
;
1015 string SrcCacheDB
= I
->SrcCacheDB
;
1016 while(I
!= PkgList
.end() &&
1017 I
->BinCacheDB
== CacheDB
&&
1018 I
->SrcCacheDB
== SrcCacheDB
)
1027 int main(int argc
, const char *argv
[])
1029 setlocale(LC_ALL
, "");
1030 CommandLine::Args Args
[] = {
1031 {'h',"help","help",0},
1032 {0,"md5","APT::FTPArchive::MD5",0},
1033 {0,"sha1","APT::FTPArchive::SHA1",0},
1034 {0,"sha256","APT::FTPArchive::SHA256",0},
1035 {'v',"version","version",0},
1036 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
1037 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
1038 {'q',"quiet","quiet",CommandLine::IntLevel
},
1039 {'q',"silent","quiet",CommandLine::IntLevel
},
1040 {0,"delink","APT::FTPArchive::DeLinkAct",0},
1041 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
1042 {0,"contents","APT::FTPArchive::Contents",0},
1043 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
1044 {'c',"config-file",0,CommandLine::ConfigFile
},
1045 {'o',"option",0,CommandLine::ArbItem
},
1047 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
1048 {"contents",&SimpleGenContents
},
1049 {"sources",&SimpleGenSources
},
1050 {"release",&SimpleGenRelease
},
1051 {"generate",&Generate
},
1056 // Parse the command line and initialize the package library
1057 CommandLine
CmdL(Args
,_config
);
1058 ParseCommandLine(CmdL
, Cmds
, Args
, &_config
, NULL
, argc
, argv
, ShowHelp
);
1060 _config
->CndSet("quiet",0);
1061 Quiet
= _config
->FindI("quiet",0);
1062 InitOutput(clog
.rdbuf());
1064 // Match the operation
1065 CmdL
.DispatchArg(Cmds
);
1067 if (_error
->empty() == false)
1069 bool Errors
= _error
->PendingError();
1070 _error
->DumpErrors();
1071 return Errors
== true?100:0;