]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
f53958ceb3f9248df121920cd024a282051f39d1
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 // ShowHelp - Show the help text /*{{{*/
608 bool ShowHelp(CommandLine
&, CommandLine::DispatchWithHelp
const *)
610 ioprintf(cout
, "%s %s (%s)\n", PACKAGE
, PACKAGE_VERSION
, COMMON_ARCH
);
611 if (_config
->FindB("version") == true)
615 _("Usage: apt-ftparchive [options] command\n"
616 "Commands: packages binarypath [overridefile [pathprefix]]\n"
617 " sources srcpath [overridefile [pathprefix]]\n"
620 " generate config [groups]\n"
623 "apt-ftparchive generates index files for Debian archives. It supports\n"
624 "many styles of generation from fully automated to functional replacements\n"
625 "for dpkg-scanpackages and dpkg-scansources\n"
627 "apt-ftparchive generates Package files from a tree of .debs. The\n"
628 "Package file contains the contents of all the control fields from\n"
629 "each package as well as the MD5 hash and filesize. An override file\n"
630 "is supported to force the value of Priority and Section.\n"
632 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
633 "The --source-override option can be used to specify a src override file\n"
635 "The 'packages' and 'sources' command should be run in the root of the\n"
636 "tree. BinaryPath should point to the base of the recursive search and \n"
637 "override file should contain the override flags. Pathprefix is\n"
638 "appended to the filename fields if present. Example usage from the \n"
640 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
641 " dists/potato/main/binary-i386/Packages\n"
644 " -h This help text\n"
645 " --md5 Control MD5 generation\n"
646 " -s=? Source override file\n"
648 " -d=? Select the optional caching database\n"
649 " --no-delink Enable delinking debug mode\n"
650 " --contents Control contents file generation\n"
651 " -c=? Read this configuration file\n"
652 " -o=? Set an arbitrary configuration option") << endl
;
657 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
658 // ---------------------------------------------------------------------
659 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
660 static bool SimpleGenPackages(CommandLine
&CmdL
)
662 if (CmdL
.FileSize() < 2)
663 return ShowHelp(CmdL
, nullptr);
666 if (CmdL
.FileSize() >= 3)
667 Override
= CmdL
.FileList
[2];
669 // Create a package writer object.
670 PackagesWriter
Packages(NULL
, NULL
, _config
->Find("APT::FTPArchive::DB"),
671 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
672 if (_error
->PendingError() == true)
675 if (CmdL
.FileSize() >= 4)
676 Packages
.PathPrefix
= CmdL
.FileList
[3];
678 // Do recursive directory searching
679 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
682 // Give some stats if asked for
683 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
684 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
689 // SimpleGenContents - Generate a Contents listing /*{{{*/
690 // ---------------------------------------------------------------------
692 static bool SimpleGenContents(CommandLine
&CmdL
)
694 if (CmdL
.FileSize() < 2)
695 return ShowHelp(CmdL
, nullptr);
697 // Create a package writer object.
698 ContentsWriter
Contents(NULL
, _config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
699 if (_error
->PendingError() == true)
702 // Do recursive directory searching
703 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
711 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
712 // ---------------------------------------------------------------------
713 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
714 static bool SimpleGenSources(CommandLine
&CmdL
)
716 if (CmdL
.FileSize() < 2)
717 return ShowHelp(CmdL
, nullptr);
720 if (CmdL
.FileSize() >= 3)
721 Override
= CmdL
.FileList
[2];
724 if (Override
.empty() == false)
725 SOverride
= Override
+ ".src";
727 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
730 // Create a package writer object.
731 SourcesWriter
Sources(NULL
, _config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
732 if (_error
->PendingError() == true)
735 if (CmdL
.FileSize() >= 4)
736 Sources
.PathPrefix
= CmdL
.FileList
[3];
738 // Do recursive directory searching
739 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
742 // Give some stats if asked for
743 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
744 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
749 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
750 // ---------------------------------------------------------------------
751 static bool SimpleGenRelease(CommandLine
&CmdL
)
753 if (CmdL
.FileSize() < 2)
754 return ShowHelp(CmdL
, nullptr);
756 string Dir
= CmdL
.FileList
[1];
758 ReleaseWriter
Release(NULL
, "");
759 Release
.DirStrip
= Dir
;
761 if (_error
->PendingError() == true)
764 if (Release
.RecursiveScan(Dir
) == false)
773 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
774 // ---------------------------------------------------------------------
775 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
776 vector
<PackageMap
> &PkgList
,
777 struct CacheDB::Stats
&SrcStats
,
778 struct CacheDB::Stats
&Stats
,
781 if (CmdL
.FileSize() <= 2)
783 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
784 if (I
->GenPackages(Setup
,Stats
) == false)
785 _error
->DumpErrors();
786 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
787 if (I
->GenSources(Setup
,SrcStats
) == false)
788 _error
->DumpErrors();
792 // Make a choice list out of the package list..
793 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
794 RxChoiceList
*End
= List
;
795 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
797 End
->UserData
= &(*I
);
798 End
->Str
= I
->BaseDir
.c_str();
801 End
->UserData
= &(*I
);
802 End
->Str
= I
->Tag
.c_str();
808 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
811 return _error
->Error(_("No selections matched"));
813 _error
->DumpErrors();
815 // Do the generation for Packages
816 for (End
= List
; End
->Str
!= 0; End
++)
818 if (End
->Hit
== false)
821 PackageMap
*I
= (PackageMap
*)End
->UserData
;
822 if (I
->PkgDone
== true)
824 if (I
->GenPackages(Setup
,Stats
) == false)
825 _error
->DumpErrors();
828 // Do the generation for Sources
829 for (End
= List
; End
->Str
!= 0; End
++)
831 if (End
->Hit
== false)
834 PackageMap
*I
= (PackageMap
*)End
->UserData
;
835 if (I
->SrcDone
== true)
837 if (I
->GenSources(Setup
,SrcStats
) == false)
838 _error
->DumpErrors();
847 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
848 // ---------------------------------------------------------------------
849 static bool DoGenerateContents(Configuration
&Setup
,
850 vector
<PackageMap
> &PkgList
,
853 c1out
<< "Packages done, Starting contents." << endl
;
855 // Sort the contents file list by date
856 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
857 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
860 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
861 I
->CntCompress
,A
) == false)
862 time(&I
->ContentsMTime
);
864 I
->ContentsMTime
= A
.st_mtime
;
866 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
868 /* Now for Contents.. The process here is to do a make-like dependency
869 check. Each contents file is verified to be newer than the package files
870 that describe the debs it indexes. Since the package files contain
871 hashes of the .debs this means they have not changed either so the
872 contents must be up to date. */
873 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",
874 std::numeric_limits
<unsigned int>::max())*1024;
875 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
877 // This record is not relevant
878 if (I
->ContentsDone
== true ||
879 I
->Contents
.empty() == true)
882 // Do not do everything if the user specified sections.
883 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
887 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
889 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
891 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
895 if (A
.st_mtime
> B
.st_mtime
)
899 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
900 MaxContentsChange
) == false)
901 _error
->DumpErrors();
904 if (MaxContentsChange
== 0)
906 c1out
<< "Hit contents update byte limit" << endl
;
915 // Generate - Full generate, using a config file /*{{{*/
916 // ---------------------------------------------------------------------
918 static bool Generate(CommandLine
&CmdL
)
920 struct CacheDB::Stats SrcStats
;
921 if (CmdL
.FileSize() < 2)
922 return ShowHelp(CmdL
, nullptr);
924 struct timeval StartTime
;
925 gettimeofday(&StartTime
,0);
926 struct CacheDB::Stats Stats
;
928 // Read the configuration file.
930 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
933 vector
<PackageMap
> PkgList
;
934 std::vector
<TranslationWriter
*> TransList
;
935 LoadTree(PkgList
, TransList
, Setup
);
936 LoadBinDir(PkgList
,Setup
);
938 // Sort by cache DB to improve IO locality.
939 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
940 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
943 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
945 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
947 UnloadTree(TransList
);
951 c1out
<< "Skipping Packages/Sources generation" << endl
;
954 // do Contents if needed
955 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
956 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
958 UnloadTree(TransList
);
962 struct timeval NewTime
;
963 gettimeofday(&NewTime
,0);
964 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
965 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
966 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
967 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
969 UnloadTree(TransList
);
974 // Clean - Clean out the databases /*{{{*/
975 // ---------------------------------------------------------------------
977 static bool Clean(CommandLine
&CmdL
)
979 if (CmdL
.FileSize() != 2)
980 return ShowHelp(CmdL
, nullptr);
982 // Read the configuration file.
984 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
986 // we don't need translation creation here
987 Setup
.Set("TreeDefault::Translation", "/dev/null");
989 vector
<PackageMap
> PkgList
;
990 std::vector
<TranslationWriter
*> TransList
;
991 LoadTree(PkgList
, TransList
, Setup
);
992 LoadBinDir(PkgList
,Setup
);
994 // Sort by cache DB to improve IO locality.
995 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
996 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
998 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1000 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1002 if(I
->BinCacheDB
!= "")
1003 c0out
<< I
->BinCacheDB
<< endl
;
1004 if(I
->SrcCacheDB
!= "")
1005 c0out
<< I
->SrcCacheDB
<< endl
;
1006 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1007 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1008 if (DB
.Clean() == false)
1009 _error
->DumpErrors();
1010 if (DB_SRC
.Clean() == false)
1011 _error
->DumpErrors();
1013 string CacheDB
= I
->BinCacheDB
;
1014 string SrcCacheDB
= I
->SrcCacheDB
;
1015 while(I
!= PkgList
.end() &&
1016 I
->BinCacheDB
== CacheDB
&&
1017 I
->SrcCacheDB
== SrcCacheDB
)
1026 std::vector
<CommandLine::DispatchWithHelp
> GetCommands() /*{{{*/
1029 {"packages",&SimpleGenPackages
, nullptr},
1030 {"contents",&SimpleGenContents
, nullptr},
1031 {"sources",&SimpleGenSources
, nullptr},
1032 {"release",&SimpleGenRelease
, nullptr},
1033 {"generate",&Generate
, nullptr},
1034 {"clean",&Clean
, nullptr},
1035 {nullptr, nullptr, nullptr}
1039 int main(int argc
, const char *argv
[]) /*{{{*/
1043 // Parse the command line and initialize the package library
1045 auto const Cmds
= ParseCommandLine(CmdL
, APT_CMD::APT_FTPARCHIVE
, &_config
, NULL
, argc
, argv
);
1047 _config
->CndSet("quiet",0);
1048 Quiet
= _config
->FindI("quiet",0);
1049 InitOutput(clog
.rdbuf());
1051 return DispatchCommandLine(CmdL
, Cmds
);