]>
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>
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
72 // Stuff for the Source File
75 string SrcExtraOverride
;
77 // Translation master file
79 TranslationWriter
*TransWriter
;
91 unsigned int DeLinkLimit
;
99 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
101 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
102 {return x
.ContentsMTime
< y
.ContentsMTime
;};
105 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
107 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
108 {return x
.BinCacheDB
< y
.BinCacheDB
;};
111 struct SrcDBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
113 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
114 {return x
.SrcCacheDB
< y
.SrcCacheDB
;};
117 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
118 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
119 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
120 bool GenContents(Configuration
&Setup
,
121 vector
<PackageMap
>::iterator Begin
,
122 vector
<PackageMap
>::iterator End
,
123 unsigned long &Left
);
125 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
126 ContentsDone(false), PkgDone(false), SrcDone(false),
131 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
132 // ---------------------------------------------------------------------
134 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
136 PathPrefix
= Block
.Find("PathPrefix");
138 if (Block
.FindB("External-Links",true) == false)
139 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit", std::numeric_limits
<unsigned int>::max());
143 PkgCompress
= Block
.Find("Packages::Compress",
144 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
145 CntCompress
= Block
.Find("Contents::Compress",
146 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
147 SrcCompress
= Block
.Find("Sources::Compress",
148 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
150 SrcExt
= Block
.Find("Sources::Extensions",
151 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
152 PkgExt
= Block
.Find("Packages::Extensions",
153 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
155 Permissions
= Setup
.FindI("Default::FileMode",0644);
157 if (FLFile
.empty() == false)
158 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
164 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
165 // ---------------------------------------------------------------------
166 /* This generates the Package File described by this object. */
167 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
169 if (PkgFile
.empty() == true)
172 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
173 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
174 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
176 struct timeval StartTime
;
177 gettimeofday(&StartTime
,0);
181 // Create a package writer object.
182 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
183 PkgCompress
,Permissions
);
184 PackagesWriter
Packages(&Comp
.Input
, TransWriter
, flCombine(CacheDir
,BinCacheDB
),
185 flCombine(OverrideDir
,BinOverride
),
186 flCombine(OverrideDir
,ExtraOverride
),
188 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
189 return _error
->Error(_("Package extension list is too long"));
190 if (_error
->PendingError() == true)
191 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
193 Packages
.PathPrefix
= PathPrefix
;
194 Packages
.DirStrip
= ArchiveDir
;
195 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
197 Packages
.LongDescription
= LongDesc
;
199 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
200 Packages
.DeLinkLimit
= DeLinkLimit
;
202 if (_error
->PendingError() == true)
203 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
205 c0out
<< ' ' << BaseDir
<< ":" << flush
;
207 // Do recursive directory searching
208 if (FLFile
.empty() == true)
210 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
215 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
219 Packages
.Output
= 0; // Just in case
221 // Finish compressing
222 unsigned long long Size
;
223 if (Comp
.Finalize(Size
) == false)
226 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
231 << SizeToStr(Size
) << "B ";
235 struct timeval NewTime
;
236 gettimeofday(&NewTime
,0);
237 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
238 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
240 c0out
<< Packages
.Stats
.Packages
<< " files " <<
241 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
242 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
243 TimeToStr((long)Delta
) << endl
;
245 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
246 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
248 Stats
.Add(Packages
.Stats
);
249 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
251 return !_error
->PendingError();
255 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
256 // ---------------------------------------------------------------------
257 /* This generates the Sources File described by this object. */
258 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
260 if (SrcFile
.empty() == true)
263 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
264 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
265 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
267 struct timeval StartTime
;
268 gettimeofday(&StartTime
,0);
272 // Create a package writer object.
273 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
274 SrcCompress
,Permissions
);
275 SourcesWriter
Sources(&Comp
.Input
, flCombine(CacheDir
, SrcCacheDB
),
276 flCombine(OverrideDir
,BinOverride
),
277 flCombine(OverrideDir
,SrcOverride
),
278 flCombine(OverrideDir
,SrcExtraOverride
));
279 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
280 return _error
->Error(_("Source extension list is too long"));
281 if (_error
->PendingError() == true)
282 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
284 Sources
.PathPrefix
= PathPrefix
;
285 Sources
.DirStrip
= ArchiveDir
;
286 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
288 Sources
.DeLinkLimit
= DeLinkLimit
;
289 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
291 if (_error
->PendingError() == true)
292 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
294 c0out
<< ' ' << BaseDir
<< ":" << flush
;
296 // Do recursive directory searching
297 if (FLFile
.empty() == true)
299 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
304 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
307 Sources
.Output
= 0; // Just in case
309 // Finish compressing
310 unsigned long long Size
;
311 if (Comp
.Finalize(Size
) == false)
314 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
319 << SizeToStr(Size
) << "B ";
323 struct timeval NewTime
;
324 gettimeofday(&NewTime
,0);
325 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
326 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
328 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
329 TimeToStr((long)Delta
) << endl
;
331 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
332 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
334 Stats
.Add(Sources
.Stats
);
335 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
337 return !_error
->PendingError();
340 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
341 // ---------------------------------------------------------------------
342 /* This generates the contents file partially described by this object.
343 It searches the given iterator range for other package files that map
344 into this contents file and includes their data as well when building. */
345 bool PackageMap::GenContents(Configuration
&Setup
,
346 vector
<PackageMap
>::iterator Begin
,
347 vector
<PackageMap
>::iterator End
,
350 if (Contents
.empty() == true)
356 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
357 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
358 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
360 struct timeval StartTime
;
361 gettimeofday(&StartTime
,0);
363 // Create a package writer object.
364 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
365 CntCompress
,Permissions
);
366 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
367 ContentsWriter
Contents(&Comp
.Input
, "", Arch
);
368 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
369 return _error
->Error(_("Package extension list is too long"));
370 if (_error
->PendingError() == true)
373 if (_error
->PendingError() == true)
376 // Write the header out.
377 if (ContentsHead
.empty() == false)
379 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
380 if (_error
->PendingError() == true)
383 unsigned long long Size
= Head
.Size();
384 unsigned char Buf
[4096];
387 unsigned long long ToRead
= Size
;
388 if (Size
> sizeof(Buf
))
389 ToRead
= sizeof(Buf
);
391 if (Head
.Read(Buf
,ToRead
) == false)
394 if (Comp
.Input
.Write(Buf
, ToRead
) == false)
395 return _error
->Errno("fwrite",_("Error writing header to contents file"));
401 /* Go over all the package file records and parse all the package
402 files associated with this contents file into one great big honking
403 memory structure, then dump the sorted version */
404 c0out
<< ' ' << this->Contents
<< ":" << flush
;
405 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
407 if (I
->Contents
!= this->Contents
)
410 Contents
.Prefix
= ArchiveDir
;
411 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
412 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
415 I
->ContentsDone
= true;
420 // Finish compressing
421 unsigned long long Size
;
422 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
425 return _error
->Error(_("Error processing contents %s"),
426 this->Contents
.c_str());
431 c0out
<< " New " << SizeToStr(Size
) << "B ";
440 struct timeval NewTime
;
441 gettimeofday(&NewTime
,0);
442 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
443 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
445 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
446 c0out
<< " Misses in Cache: " << Contents
.Stats
.Misses
<< endl
;
448 c0out
<< Contents
.Stats
.Packages
<< " files " <<
449 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
450 TimeToStr((long)Delta
) << endl
;
456 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
457 // ---------------------------------------------------------------------
458 /* This populates the PkgList with all the possible permutations of the
459 section/arch lists. */
460 static void LoadTree(vector
<PackageMap
> &PkgList
, std::vector
<TranslationWriter
*> &TransList
, Configuration
&Setup
)
463 string DDir
= Setup
.Find("TreeDefault::Directory",
464 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
465 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
466 "$(DIST)/$(SECTION)/source/");
467 string DPkg
= Setup
.Find("TreeDefault::Packages",
468 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
469 string DTrans
= Setup
.Find("TreeDefault::Translation",
470 "$(DIST)/$(SECTION)/i18n/Translation-en");
471 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
472 "$(DIST)/$(SECTION)/");
473 string DContents
= Setup
.Find("TreeDefault::Contents",
474 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
475 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
476 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
477 "packages-$(ARCH).db");
478 string SrcDBCache
= Setup
.Find("TreeDefault::SrcCacheDB",
479 "sources-$(SECTION).db");
480 string DSources
= Setup
.Find("TreeDefault::Sources",
481 "$(DIST)/$(SECTION)/source/Sources");
482 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
483 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
485 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
487 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
488 _config
->FindB("APT::FTPArchive::LongDescription", true));
489 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
491 // Process 'tree' type sections
492 const Configuration::Item
*Top
= Setup
.Tree("tree");
493 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
495 Configuration
Block(Top
);
496 string Dist
= Top
->Tag
;
498 // Parse the sections
499 string Tmp
= Block
.Find("Sections");
500 const char *Sections
= Tmp
.c_str();
502 while (ParseQuoteWord(Sections
,Section
) == true)
505 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
506 {"$(SECTION)",&Section
},
509 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
510 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
511 TranslationWriter
*TransWriter
= NULL
;
513 string
const Tmp2
= Block
.Find("Architectures");
514 const char *Archs
= Tmp2
.c_str();
515 while (ParseQuoteWord(Archs
,Arch
) == true)
518 Itm
.Permissions
= Perms
;
519 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
520 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
522 if (stringcasecmp(Arch
,"source") == 0)
524 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
525 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
526 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
527 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
528 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
529 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
530 Itm
.SrcCacheDB
= SubstVar(Block
.Find("SrcCacheDB",SrcDBCache
.c_str()),Vars
);
534 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
535 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
536 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
537 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
539 Itm
.LongDesc
= LongDesc
;
540 if (TransWriter
== NULL
&& DTrans
.empty() == false && LongDesc
== false && DTrans
!= "/dev/null")
542 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
543 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
544 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
545 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
546 TransList
.push_back(TransWriter
);
548 Itm
.TransWriter
= TransWriter
;
549 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
550 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
551 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
552 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
555 Itm
.GetGeneral(Setup
,Block
);
556 PkgList
.push_back(Itm
);
564 static void UnloadTree(std::vector
<TranslationWriter
*> const &Trans
) /*{{{*/
566 for (std::vector
<TranslationWriter
*>::const_reverse_iterator T
= Trans
.rbegin(); T
!= Trans
.rend(); ++T
)
570 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
571 // ---------------------------------------------------------------------
573 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
575 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
577 // Process 'bindirectory' type sections
578 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
579 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
581 Configuration
Block(Top
);
584 Itm
.PkgFile
= Block
.Find("Packages");
585 Itm
.SrcFile
= Block
.Find("Sources");
586 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
587 Itm
.SrcCacheDB
= Block
.Find("SrcCacheDB");
588 Itm
.BinOverride
= Block
.Find("BinOverride");
589 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
590 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
591 Itm
.SrcOverride
= Block
.Find("SrcOverride");
592 Itm
.BaseDir
= Top
->Tag
;
593 Itm
.FLFile
= Block
.Find("FileList");
594 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
595 Itm
.Contents
= Block
.Find("Contents");
596 Itm
.ContentsHead
= Block
.Find("Contents::Header");
597 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
599 Itm
.GetGeneral(Setup
,Block
);
600 PkgList
.push_back(Itm
);
607 bool ShowHelp(CommandLine
&) /*{{{*/
610 _("Usage: apt-ftparchive [options] command\n"
611 "Commands: packages binarypath [overridefile [pathprefix]]\n"
612 " sources srcpath [overridefile [pathprefix]]\n"
615 " generate config [groups]\n"
618 "apt-ftparchive generates index files for Debian archives. It supports\n"
619 "many styles of generation from fully automated to functional replacements\n"
620 "for dpkg-scanpackages and dpkg-scansources\n"
622 "apt-ftparchive generates Package files from a tree of .debs. The\n"
623 "Package file contains the contents of all the control fields from\n"
624 "each package as well as the MD5 hash and filesize. An override file\n"
625 "is supported to force the value of Priority and Section.\n"
627 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
628 "The --source-override option can be used to specify a src override file\n"
630 "The 'packages' and 'sources' command should be run in the root of the\n"
631 "tree. BinaryPath should point to the base of the recursive search and \n"
632 "override file should contain the override flags. Pathprefix is\n"
633 "appended to the filename fields if present. Example usage from the \n"
635 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
636 " dists/potato/main/binary-i386/Packages\n"
639 " -h This help text\n"
640 " --md5 Control MD5 generation\n"
641 " -s=? Source override file\n"
643 " -d=? Select the optional caching database\n"
644 " --no-delink Enable delinking debug mode\n"
645 " --contents Control contents file generation\n"
646 " -c=? Read this configuration file\n"
647 " -o=? Set an arbitrary configuration option") << endl
;
651 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
652 // ---------------------------------------------------------------------
653 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
654 static bool SimpleGenPackages(CommandLine
&CmdL
)
656 if (CmdL
.FileSize() < 2)
657 return ShowHelp(CmdL
);
660 if (CmdL
.FileSize() >= 3)
661 Override
= CmdL
.FileList
[2];
663 // Create a package writer object.
664 PackagesWriter
Packages(NULL
, NULL
, _config
->Find("APT::FTPArchive::DB"),
665 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
666 if (_error
->PendingError() == true)
669 if (CmdL
.FileSize() >= 4)
670 Packages
.PathPrefix
= CmdL
.FileList
[3];
672 // Do recursive directory searching
673 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
676 // Give some stats if asked for
677 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
678 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
683 // SimpleGenContents - Generate a Contents listing /*{{{*/
684 // ---------------------------------------------------------------------
686 static bool SimpleGenContents(CommandLine
&CmdL
)
688 if (CmdL
.FileSize() < 2)
689 return ShowHelp(CmdL
);
691 // Create a package writer object.
692 ContentsWriter
Contents(NULL
, _config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
693 if (_error
->PendingError() == true)
696 // Do recursive directory searching
697 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
705 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
706 // ---------------------------------------------------------------------
707 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
708 static bool SimpleGenSources(CommandLine
&CmdL
)
710 if (CmdL
.FileSize() < 2)
711 return ShowHelp(CmdL
);
714 if (CmdL
.FileSize() >= 3)
715 Override
= CmdL
.FileList
[2];
718 if (Override
.empty() == false)
719 SOverride
= Override
+ ".src";
721 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
724 // Create a package writer object.
725 SourcesWriter
Sources(NULL
, _config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
726 if (_error
->PendingError() == true)
729 if (CmdL
.FileSize() >= 4)
730 Sources
.PathPrefix
= CmdL
.FileList
[3];
732 // Do recursive directory searching
733 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
736 // Give some stats if asked for
737 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
738 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
743 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
744 // ---------------------------------------------------------------------
745 static bool SimpleGenRelease(CommandLine
&CmdL
)
747 if (CmdL
.FileSize() < 2)
748 return ShowHelp(CmdL
);
750 string Dir
= CmdL
.FileList
[1];
752 ReleaseWriter
Release(NULL
, "");
753 Release
.DirStrip
= Dir
;
755 if (_error
->PendingError() == true)
758 if (Release
.RecursiveScan(Dir
) == false)
767 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
768 // ---------------------------------------------------------------------
769 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
770 vector
<PackageMap
> &PkgList
,
771 struct CacheDB::Stats
&SrcStats
,
772 struct CacheDB::Stats
&Stats
,
775 if (CmdL
.FileSize() <= 2)
777 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
778 if (I
->GenPackages(Setup
,Stats
) == false)
779 _error
->DumpErrors();
780 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
781 if (I
->GenSources(Setup
,SrcStats
) == false)
782 _error
->DumpErrors();
786 // Make a choice list out of the package list..
787 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
788 RxChoiceList
*End
= List
;
789 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
791 End
->UserData
= &(*I
);
792 End
->Str
= I
->BaseDir
.c_str();
795 End
->UserData
= &(*I
);
796 End
->Str
= I
->Tag
.c_str();
802 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
805 return _error
->Error(_("No selections matched"));
807 _error
->DumpErrors();
809 // Do the generation for Packages
810 for (End
= List
; End
->Str
!= 0; End
++)
812 if (End
->Hit
== false)
815 PackageMap
*I
= (PackageMap
*)End
->UserData
;
816 if (I
->PkgDone
== true)
818 if (I
->GenPackages(Setup
,Stats
) == false)
819 _error
->DumpErrors();
822 // Do the generation for Sources
823 for (End
= List
; End
->Str
!= 0; End
++)
825 if (End
->Hit
== false)
828 PackageMap
*I
= (PackageMap
*)End
->UserData
;
829 if (I
->SrcDone
== true)
831 if (I
->GenSources(Setup
,SrcStats
) == false)
832 _error
->DumpErrors();
841 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
842 // ---------------------------------------------------------------------
843 static bool DoGenerateContents(Configuration
&Setup
,
844 vector
<PackageMap
> &PkgList
,
847 c1out
<< "Packages done, Starting contents." << endl
;
849 // Sort the contents file list by date
850 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
851 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
854 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
855 I
->CntCompress
,A
) == false)
856 time(&I
->ContentsMTime
);
858 I
->ContentsMTime
= A
.st_mtime
;
860 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
862 /* Now for Contents.. The process here is to do a make-like dependency
863 check. Each contents file is verified to be newer than the package files
864 that describe the debs it indexes. Since the package files contain
865 hashes of the .debs this means they have not changed either so the
866 contents must be up to date. */
867 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",
868 std::numeric_limits
<unsigned int>::max())*1024;
869 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
871 // This record is not relevant
872 if (I
->ContentsDone
== true ||
873 I
->Contents
.empty() == true)
876 // Do not do everything if the user specified sections.
877 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
881 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
883 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
885 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
889 if (A
.st_mtime
> B
.st_mtime
)
893 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
894 MaxContentsChange
) == false)
895 _error
->DumpErrors();
898 if (MaxContentsChange
== 0)
900 c1out
<< "Hit contents update byte limit" << endl
;
909 // Generate - Full generate, using a config file /*{{{*/
910 // ---------------------------------------------------------------------
912 static bool Generate(CommandLine
&CmdL
)
914 struct CacheDB::Stats SrcStats
;
915 if (CmdL
.FileSize() < 2)
916 return ShowHelp(CmdL
);
918 struct timeval StartTime
;
919 gettimeofday(&StartTime
,0);
920 struct CacheDB::Stats Stats
;
922 // Read the configuration file.
924 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
927 vector
<PackageMap
> PkgList
;
928 std::vector
<TranslationWriter
*> TransList
;
929 LoadTree(PkgList
, TransList
, Setup
);
930 LoadBinDir(PkgList
,Setup
);
932 // Sort by cache DB to improve IO locality.
933 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
934 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
937 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
939 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
941 UnloadTree(TransList
);
945 c1out
<< "Skipping Packages/Sources generation" << endl
;
948 // do Contents if needed
949 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
950 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
952 UnloadTree(TransList
);
956 struct timeval NewTime
;
957 gettimeofday(&NewTime
,0);
958 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
959 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
960 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
961 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
963 UnloadTree(TransList
);
968 // Clean - Clean out the databases /*{{{*/
969 // ---------------------------------------------------------------------
971 static bool Clean(CommandLine
&CmdL
)
973 if (CmdL
.FileSize() != 2)
974 return ShowHelp(CmdL
);
976 // Read the configuration file.
978 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
980 // we don't need translation creation here
981 Setup
.Set("TreeDefault::Translation", "/dev/null");
983 vector
<PackageMap
> PkgList
;
984 std::vector
<TranslationWriter
*> TransList
;
985 LoadTree(PkgList
, TransList
, Setup
);
986 LoadBinDir(PkgList
,Setup
);
988 // Sort by cache DB to improve IO locality.
989 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
990 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
992 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
994 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
996 if(I
->BinCacheDB
!= "")
997 c0out
<< I
->BinCacheDB
<< endl
;
998 if(I
->SrcCacheDB
!= "")
999 c0out
<< I
->SrcCacheDB
<< endl
;
1000 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1001 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1002 if (DB
.Clean() == false)
1003 _error
->DumpErrors();
1004 if (DB_SRC
.Clean() == false)
1005 _error
->DumpErrors();
1007 string CacheDB
= I
->BinCacheDB
;
1008 string SrcCacheDB
= I
->SrcCacheDB
;
1009 while(I
!= PkgList
.end() &&
1010 I
->BinCacheDB
== CacheDB
&&
1011 I
->SrcCacheDB
== SrcCacheDB
)
1020 std::vector
<aptDispatchWithHelp
> GetCommands() /*{{{*/
1023 {"packages",&SimpleGenPackages
, nullptr},
1024 {"contents",&SimpleGenContents
, nullptr},
1025 {"sources",&SimpleGenSources
, nullptr},
1026 {"release",&SimpleGenRelease
, nullptr},
1027 {"generate",&Generate
, nullptr},
1028 {"clean",&Clean
, nullptr},
1029 {nullptr, nullptr, nullptr}
1033 int main(int argc
, const char *argv
[]) /*{{{*/
1037 // Parse the command line and initialize the package library
1039 auto const Cmds
= ParseCommandLine(CmdL
, APT_CMD::APT_FTPARCHIVE
, &_config
, NULL
, argc
, argv
);
1041 _config
->CndSet("quiet",0);
1042 Quiet
= _config
->FindI("quiet",0);
1043 InitOutput(clog
.rdbuf());
1045 return DispatchCommandLine(CmdL
, Cmds
);