]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
c7e38badfc1d9d166f702859e82b03c4e38fb299
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>
24 #include <apt-private/private-main.h>
40 #include "apt-ftparchive.h"
41 #include "multicompress.h"
50 // struct PackageMap - List of all package files in the config file /*{{{*/
51 // ---------------------------------------------------------------------
57 string InternalPrefix
;
62 // Stuff for the Package File
69 // We generate for this given arch
73 // Stuff for the Source File
76 string SrcExtraOverride
;
78 // Translation master file
80 TranslationWriter
*TransWriter
;
92 unsigned int DeLinkLimit
;
100 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
102 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
103 {return x
.ContentsMTime
< y
.ContentsMTime
;};
106 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
108 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
109 {return x
.BinCacheDB
< y
.BinCacheDB
;};
112 struct SrcDBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
114 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
115 {return x
.SrcCacheDB
< y
.SrcCacheDB
;};
118 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
119 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
120 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
121 bool GenContents(Configuration
&Setup
,
122 vector
<PackageMap
>::iterator Begin
,
123 vector
<PackageMap
>::iterator End
,
124 unsigned long &Left
);
126 PackageMap() : IncludeArchAll(true), LongDesc(true), TransWriter(NULL
),
127 DeLinkLimit(0), Permissions(1), ContentsDone(false),
128 PkgDone(false), SrcDone(false), ContentsMTime(0) {};
132 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
133 // ---------------------------------------------------------------------
135 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
137 PathPrefix
= Block
.Find("PathPrefix");
139 if (Block
.FindB("External-Links",true) == false)
140 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit", std::numeric_limits
<unsigned int>::max());
144 PkgCompress
= Block
.Find("Packages::Compress",
145 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
146 CntCompress
= Block
.Find("Contents::Compress",
147 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
148 SrcCompress
= Block
.Find("Sources::Compress",
149 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
151 SrcExt
= Block
.Find("Sources::Extensions",
152 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
153 PkgExt
= Block
.Find("Packages::Extensions",
154 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
156 Permissions
= Setup
.FindI("Default::FileMode",0644);
158 if (FLFile
.empty() == false)
159 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
165 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
166 // ---------------------------------------------------------------------
167 /* This generates the Package File described by this object. */
168 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
170 if (PkgFile
.empty() == true)
173 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
174 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
175 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
177 struct timeval StartTime
;
178 gettimeofday(&StartTime
,0);
182 // Create a package writer object.
183 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
184 PkgCompress
,Permissions
);
185 PackagesWriter
Packages(&Comp
.Input
, TransWriter
, flCombine(CacheDir
,BinCacheDB
),
186 flCombine(OverrideDir
,BinOverride
),
187 flCombine(OverrideDir
,ExtraOverride
),
188 Arch
, IncludeArchAll
);
189 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
190 return _error
->Error(_("Package extension list is too long"));
191 if (_error
->PendingError() == true)
192 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
194 Packages
.PathPrefix
= PathPrefix
;
195 Packages
.DirStrip
= ArchiveDir
;
196 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
198 Packages
.LongDescription
= LongDesc
;
200 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
201 Packages
.DeLinkLimit
= DeLinkLimit
;
203 if (_error
->PendingError() == true)
204 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
206 c0out
<< ' ' << BaseDir
<< ":" << flush
;
208 // Do recursive directory searching
209 if (FLFile
.empty() == true)
211 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
216 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
220 Packages
.Output
= 0; // Just in case
222 // Finish compressing
223 unsigned long long Size
;
224 if (Comp
.Finalize(Size
) == false)
227 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
232 << SizeToStr(Size
) << "B ";
236 struct timeval NewTime
;
237 gettimeofday(&NewTime
,0);
238 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
239 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
241 c0out
<< Packages
.Stats
.Packages
<< " files " <<
242 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
243 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
244 TimeToStr((long)Delta
) << endl
;
246 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
247 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
249 Stats
.Add(Packages
.Stats
);
250 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
252 return !_error
->PendingError();
256 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
257 // ---------------------------------------------------------------------
258 /* This generates the Sources File described by this object. */
259 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
261 if (SrcFile
.empty() == true)
264 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
265 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
266 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
268 struct timeval StartTime
;
269 gettimeofday(&StartTime
,0);
273 // Create a package writer object.
274 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
275 SrcCompress
,Permissions
);
276 SourcesWriter
Sources(&Comp
.Input
, flCombine(CacheDir
, SrcCacheDB
),
277 flCombine(OverrideDir
,BinOverride
),
278 flCombine(OverrideDir
,SrcOverride
),
279 flCombine(OverrideDir
,SrcExtraOverride
));
280 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
281 return _error
->Error(_("Source extension list is too long"));
282 if (_error
->PendingError() == true)
283 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
285 Sources
.PathPrefix
= PathPrefix
;
286 Sources
.DirStrip
= ArchiveDir
;
287 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
289 Sources
.DeLinkLimit
= DeLinkLimit
;
290 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
292 if (_error
->PendingError() == true)
293 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
295 c0out
<< ' ' << BaseDir
<< ":" << flush
;
297 // Do recursive directory searching
298 if (FLFile
.empty() == true)
300 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
305 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
308 Sources
.Output
= 0; // Just in case
310 // Finish compressing
311 unsigned long long Size
;
312 if (Comp
.Finalize(Size
) == false)
315 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
320 << SizeToStr(Size
) << "B ";
324 struct timeval NewTime
;
325 gettimeofday(&NewTime
,0);
326 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
327 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
329 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
330 TimeToStr((long)Delta
) << endl
;
332 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
333 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
335 Stats
.Add(Sources
.Stats
);
336 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
338 return !_error
->PendingError();
341 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
342 // ---------------------------------------------------------------------
343 /* This generates the contents file partially described by this object.
344 It searches the given iterator range for other package files that map
345 into this contents file and includes their data as well when building. */
346 bool PackageMap::GenContents(Configuration
&Setup
,
347 vector
<PackageMap
>::iterator Begin
,
348 vector
<PackageMap
>::iterator End
,
351 if (Contents
.empty() == true)
357 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
358 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
359 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
361 struct timeval StartTime
;
362 gettimeofday(&StartTime
,0);
364 // Create a package writer object.
365 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
366 CntCompress
,Permissions
);
367 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
368 ContentsWriter
Contents(&Comp
.Input
, "", Arch
, IncludeArchAll
);
369 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
370 return _error
->Error(_("Package extension list is too long"));
371 if (_error
->PendingError() == true)
374 if (_error
->PendingError() == true)
377 // Write the header out.
378 if (ContentsHead
.empty() == false)
380 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
381 if (_error
->PendingError() == true)
384 unsigned long long Size
= Head
.Size();
385 unsigned char Buf
[4096];
388 unsigned long long ToRead
= Size
;
389 if (Size
> sizeof(Buf
))
390 ToRead
= sizeof(Buf
);
392 if (Head
.Read(Buf
,ToRead
) == false)
395 if (Comp
.Input
.Write(Buf
, ToRead
) == false)
396 return _error
->Errno("fwrite",_("Error writing header to contents file"));
402 /* Go over all the package file records and parse all the package
403 files associated with this contents file into one great big honking
404 memory structure, then dump the sorted version */
405 c0out
<< ' ' << this->Contents
<< ":" << flush
;
406 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
408 if (I
->Contents
!= this->Contents
)
411 Contents
.Prefix
= ArchiveDir
;
412 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
413 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
416 I
->ContentsDone
= true;
421 // Finish compressing
422 unsigned long long Size
;
423 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
426 return _error
->Error(_("Error processing contents %s"),
427 this->Contents
.c_str());
432 c0out
<< " New " << SizeToStr(Size
) << "B ";
441 struct timeval NewTime
;
442 gettimeofday(&NewTime
,0);
443 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
444 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
446 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
447 c0out
<< " Misses in Cache: " << Contents
.Stats
.Misses
<< endl
;
449 c0out
<< Contents
.Stats
.Packages
<< " files " <<
450 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
451 TimeToStr((long)Delta
) << endl
;
457 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
458 // ---------------------------------------------------------------------
459 /* This populates the PkgList with all the possible permutations of the
460 section/arch lists. */
461 static void LoadTree(vector
<PackageMap
> &PkgList
, std::vector
<TranslationWriter
*> &TransList
, Configuration
&Setup
)
464 string DDir
= Setup
.Find("TreeDefault::Directory",
465 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
466 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
467 "$(DIST)/$(SECTION)/source/");
468 string DPkg
= Setup
.Find("TreeDefault::Packages",
469 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
470 string DTrans
= Setup
.Find("TreeDefault::Translation",
471 "$(DIST)/$(SECTION)/i18n/Translation-en");
472 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
473 "$(DIST)/$(SECTION)/");
474 string DContents
= Setup
.Find("TreeDefault::Contents",
475 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
476 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
477 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
478 "packages-$(ARCH).db");
479 string SrcDBCache
= Setup
.Find("TreeDefault::SrcCacheDB",
480 "sources-$(SECTION).db");
481 string DSources
= Setup
.Find("TreeDefault::Sources",
482 "$(DIST)/$(SECTION)/source/Sources");
483 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
484 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
486 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
488 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
489 _config
->FindB("APT::FTPArchive::LongDescription", true));
490 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
491 bool const ConfIncludeArchAllExists
= _config
->Exists("APT::FTPArchive::IncludeArchitectureAll");
492 bool const ConfIncludeArchAll
= _config
->FindB("APT::FTPArchive::IncludeArchitectureAll", true);
494 // Process 'tree' type sections
495 const Configuration::Item
*Top
= Setup
.Tree("tree");
496 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
498 Configuration
Block(Top
);
499 string Dist
= Top
->Tag
;
501 // Parse the sections
502 string Tmp
= Block
.Find("Sections");
503 const char *Sections
= Tmp
.c_str();
505 while (ParseQuoteWord(Sections
,Section
) == true)
507 struct SubstVar Vars
[] = {{"$(DIST)",&Dist
},
508 {"$(SECTION)",&Section
},
511 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
512 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
513 TranslationWriter
*TransWriter
= nullptr;
515 std::string Tmp2
= Block
.Find("Architectures");
516 std::transform(Tmp2
.begin(), Tmp2
.end(), Tmp2
.begin(), ::tolower
);
517 std::vector
<std::string
> const Archs
= VectorizeString(Tmp2
, ' ');
519 if (ConfIncludeArchAllExists
== true)
520 IncludeArchAll
= ConfIncludeArchAll
;
522 IncludeArchAll
= std::find(Archs
.begin(), Archs
.end(), "all") == Archs
.end();
523 for (auto const& Arch
: Archs
)
525 if (Arch
.empty()) continue;
526 Vars
[2].Contents
= &Arch
;
528 Itm
.Permissions
= Perms
;
529 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
530 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
532 if (Arch
== "source")
534 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
535 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
536 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
537 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
538 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
539 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
540 Itm
.SrcCacheDB
= SubstVar(Block
.Find("SrcCacheDB",SrcDBCache
.c_str()),Vars
);
544 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
545 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
546 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
547 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
549 Itm
.IncludeArchAll
= IncludeArchAll
;
550 Itm
.LongDesc
= LongDesc
;
551 if (TransWriter
== NULL
&& DTrans
.empty() == false && LongDesc
== false && DTrans
!= "/dev/null")
553 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
554 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
555 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
556 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
557 TransList
.push_back(TransWriter
);
559 Itm
.TransWriter
= TransWriter
;
560 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
561 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
562 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
563 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
566 Itm
.GetGeneral(Setup
,Block
);
567 PkgList
.push_back(Itm
);
575 static void UnloadTree(std::vector
<TranslationWriter
*> const &Trans
) /*{{{*/
577 for (std::vector
<TranslationWriter
*>::const_reverse_iterator T
= Trans
.rbegin(); T
!= Trans
.rend(); ++T
)
581 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
582 // ---------------------------------------------------------------------
584 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
586 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
588 // Process 'bindirectory' type sections
589 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
590 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
592 Configuration
Block(Top
);
595 Itm
.PkgFile
= Block
.Find("Packages");
596 Itm
.SrcFile
= Block
.Find("Sources");
597 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
598 Itm
.SrcCacheDB
= Block
.Find("SrcCacheDB");
599 Itm
.BinOverride
= Block
.Find("BinOverride");
600 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
601 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
602 Itm
.SrcOverride
= Block
.Find("SrcOverride");
603 Itm
.BaseDir
= Top
->Tag
;
604 Itm
.FLFile
= Block
.Find("FileList");
605 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
606 Itm
.Contents
= Block
.Find("Contents");
607 Itm
.ContentsHead
= Block
.Find("Contents::Header");
608 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
610 Itm
.GetGeneral(Setup
,Block
);
611 PkgList
.push_back(Itm
);
618 bool ShowHelp(CommandLine
&) /*{{{*/
621 _("Usage: apt-ftparchive [options] command\n"
622 "Commands: packages binarypath [overridefile [pathprefix]]\n"
623 " sources srcpath [overridefile [pathprefix]]\n"
626 " generate config [groups]\n"
629 "apt-ftparchive generates index files for Debian archives. It supports\n"
630 "many styles of generation from fully automated to functional replacements\n"
631 "for dpkg-scanpackages and dpkg-scansources\n"
633 "apt-ftparchive generates Package files from a tree of .debs. The\n"
634 "Package file contains the contents of all the control fields from\n"
635 "each package as well as the MD5 hash and filesize. An override file\n"
636 "is supported to force the value of Priority and Section.\n"
638 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
639 "The --source-override option can be used to specify a src override file\n"
641 "The 'packages' and 'sources' command should be run in the root of the\n"
642 "tree. BinaryPath should point to the base of the recursive search and \n"
643 "override file should contain the override flags. Pathprefix is\n"
644 "appended to the filename fields if present. Example usage from the \n"
646 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
647 " dists/potato/main/binary-i386/Packages\n"
650 " -h This help text\n"
651 " --md5 Control MD5 generation\n"
652 " -s=? Source override file\n"
654 " -d=? Select the optional caching database\n"
655 " --no-delink Enable delinking debug mode\n"
656 " --contents Control contents file generation\n"
657 " -c=? Read this configuration file\n"
658 " -o=? Set an arbitrary configuration option") << endl
;
662 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
663 // ---------------------------------------------------------------------
664 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
665 static bool SimpleGenPackages(CommandLine
&CmdL
)
667 if (CmdL
.FileSize() < 2)
668 return ShowHelp(CmdL
);
671 if (CmdL
.FileSize() >= 3)
672 Override
= CmdL
.FileList
[2];
674 // Create a package writer object.
675 PackagesWriter
Packages(NULL
, NULL
, _config
->Find("APT::FTPArchive::DB"),
676 Override
, "", _config
->Find("APT::FTPArchive::Architecture"),
677 _config
->FindB("APT::FTPArchive::IncludeArchitectureAll", true));
678 if (_error
->PendingError() == true)
681 if (CmdL
.FileSize() >= 4)
682 Packages
.PathPrefix
= CmdL
.FileList
[3];
684 // Do recursive directory searching
685 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
688 // Give some stats if asked for
689 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
690 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
695 // SimpleGenContents - Generate a Contents listing /*{{{*/
696 // ---------------------------------------------------------------------
698 static bool SimpleGenContents(CommandLine
&CmdL
)
700 if (CmdL
.FileSize() < 2)
701 return ShowHelp(CmdL
);
703 // Create a package writer object.
704 ContentsWriter
Contents(NULL
, _config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
705 if (_error
->PendingError() == true)
708 // Do recursive directory searching
709 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
717 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
718 // ---------------------------------------------------------------------
719 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
720 static bool SimpleGenSources(CommandLine
&CmdL
)
722 if (CmdL
.FileSize() < 2)
723 return ShowHelp(CmdL
);
726 if (CmdL
.FileSize() >= 3)
727 Override
= CmdL
.FileList
[2];
730 if (Override
.empty() == false)
731 SOverride
= Override
+ ".src";
733 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
736 // Create a package writer object.
737 SourcesWriter
Sources(NULL
, _config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
738 if (_error
->PendingError() == true)
741 if (CmdL
.FileSize() >= 4)
742 Sources
.PathPrefix
= CmdL
.FileList
[3];
744 // Do recursive directory searching
745 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
748 // Give some stats if asked for
749 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
750 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
755 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
756 // ---------------------------------------------------------------------
757 static bool SimpleGenRelease(CommandLine
&CmdL
)
759 if (CmdL
.FileSize() < 2)
760 return ShowHelp(CmdL
);
762 string Dir
= CmdL
.FileList
[1];
764 ReleaseWriter
Release(NULL
, "");
765 Release
.DirStrip
= Dir
;
767 if (_error
->PendingError() == true)
770 if (Release
.RecursiveScan(Dir
) == false)
779 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
780 // ---------------------------------------------------------------------
781 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
782 vector
<PackageMap
> &PkgList
,
783 struct CacheDB::Stats
&SrcStats
,
784 struct CacheDB::Stats
&Stats
,
787 if (CmdL
.FileSize() <= 2)
789 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
790 if (I
->GenPackages(Setup
,Stats
) == false)
791 _error
->DumpErrors();
792 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
793 if (I
->GenSources(Setup
,SrcStats
) == false)
794 _error
->DumpErrors();
798 // Make a choice list out of the package list..
799 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
800 RxChoiceList
*End
= List
;
801 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
803 End
->UserData
= &(*I
);
804 End
->Str
= I
->BaseDir
.c_str();
807 End
->UserData
= &(*I
);
808 End
->Str
= I
->Tag
.c_str();
814 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
817 return _error
->Error(_("No selections matched"));
819 _error
->DumpErrors();
821 // Do the generation for Packages
822 for (End
= List
; End
->Str
!= 0; End
++)
824 if (End
->Hit
== false)
827 PackageMap
*I
= (PackageMap
*)End
->UserData
;
828 if (I
->PkgDone
== true)
830 if (I
->GenPackages(Setup
,Stats
) == false)
831 _error
->DumpErrors();
834 // Do the generation for Sources
835 for (End
= List
; End
->Str
!= 0; End
++)
837 if (End
->Hit
== false)
840 PackageMap
*I
= (PackageMap
*)End
->UserData
;
841 if (I
->SrcDone
== true)
843 if (I
->GenSources(Setup
,SrcStats
) == false)
844 _error
->DumpErrors();
853 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
854 // ---------------------------------------------------------------------
855 static bool DoGenerateContents(Configuration
&Setup
,
856 vector
<PackageMap
> &PkgList
,
859 c1out
<< "Packages done, Starting contents." << endl
;
861 // Sort the contents file list by date
862 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
863 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
866 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
867 I
->CntCompress
,A
) == false)
868 time(&I
->ContentsMTime
);
870 I
->ContentsMTime
= A
.st_mtime
;
872 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
874 /* Now for Contents.. The process here is to do a make-like dependency
875 check. Each contents file is verified to be newer than the package files
876 that describe the debs it indexes. Since the package files contain
877 hashes of the .debs this means they have not changed either so the
878 contents must be up to date. */
879 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",
880 std::numeric_limits
<unsigned int>::max())*1024;
881 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
883 // This record is not relevant
884 if (I
->ContentsDone
== true ||
885 I
->Contents
.empty() == true)
888 // Do not do everything if the user specified sections.
889 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
893 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
895 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
897 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
901 if (A
.st_mtime
> B
.st_mtime
)
905 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
906 MaxContentsChange
) == false)
907 _error
->DumpErrors();
910 if (MaxContentsChange
== 0)
912 c1out
<< "Hit contents update byte limit" << endl
;
921 // Generate - Full generate, using a config file /*{{{*/
922 // ---------------------------------------------------------------------
924 static bool Generate(CommandLine
&CmdL
)
926 struct CacheDB::Stats SrcStats
;
927 if (CmdL
.FileSize() < 2)
928 return ShowHelp(CmdL
);
930 struct timeval StartTime
;
931 gettimeofday(&StartTime
,0);
932 struct CacheDB::Stats Stats
;
934 // Read the configuration file.
936 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
939 vector
<PackageMap
> PkgList
;
940 std::vector
<TranslationWriter
*> TransList
;
941 LoadTree(PkgList
, TransList
, Setup
);
942 LoadBinDir(PkgList
,Setup
);
944 // Sort by cache DB to improve IO locality.
945 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
946 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
949 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
951 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
953 UnloadTree(TransList
);
957 c1out
<< "Skipping Packages/Sources generation" << endl
;
960 // do Contents if needed
961 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
962 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
964 UnloadTree(TransList
);
968 struct timeval NewTime
;
969 gettimeofday(&NewTime
,0);
970 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
971 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
972 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
973 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
975 UnloadTree(TransList
);
980 // Clean - Clean out the databases /*{{{*/
981 // ---------------------------------------------------------------------
983 static bool Clean(CommandLine
&CmdL
)
985 if (CmdL
.FileSize() != 2)
986 return ShowHelp(CmdL
);
988 // Read the configuration file.
990 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
992 // we don't need translation creation here
993 Setup
.Set("TreeDefault::Translation", "/dev/null");
995 vector
<PackageMap
> PkgList
;
996 std::vector
<TranslationWriter
*> TransList
;
997 LoadTree(PkgList
, TransList
, Setup
);
998 LoadBinDir(PkgList
,Setup
);
1000 // Sort by cache DB to improve IO locality.
1001 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
1002 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
1004 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1006 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1008 if(I
->BinCacheDB
!= "")
1009 c0out
<< I
->BinCacheDB
<< endl
;
1010 if(I
->SrcCacheDB
!= "")
1011 c0out
<< I
->SrcCacheDB
<< endl
;
1012 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1013 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1014 if (DB
.Clean() == false)
1015 _error
->DumpErrors();
1016 if (DB_SRC
.Clean() == false)
1017 _error
->DumpErrors();
1019 string CacheDB
= I
->BinCacheDB
;
1020 string SrcCacheDB
= I
->SrcCacheDB
;
1021 while(I
!= PkgList
.end() &&
1022 I
->BinCacheDB
== CacheDB
&&
1023 I
->SrcCacheDB
== SrcCacheDB
)
1032 std::vector
<aptDispatchWithHelp
> GetCommands() /*{{{*/
1035 {"packages",&SimpleGenPackages
, nullptr},
1036 {"contents",&SimpleGenContents
, nullptr},
1037 {"sources",&SimpleGenSources
, nullptr},
1038 {"release",&SimpleGenRelease
, nullptr},
1039 {"generate",&Generate
, nullptr},
1040 {"clean",&Clean
, nullptr},
1041 {nullptr, nullptr, nullptr}
1045 int main(int argc
, const char *argv
[]) /*{{{*/
1049 // Parse the command line and initialize the package library
1051 auto const Cmds
= ParseCommandLine(CmdL
, APT_CMD::APT_FTPARCHIVE
, &_config
, NULL
, argc
, argv
);
1053 _config
->CndSet("quiet",0);
1054 Quiet
= _config
->FindI("quiet",0);
1055 InitOutput(clog
.rdbuf());
1057 return DispatchCommandLine(CmdL
, Cmds
);