]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
ca03080ca221c584119220accb9a5f660000666b
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 // ---------------------------------------------------------------------
610 static bool ShowHelp(CommandLine
&, CommandLine::DispatchWithHelp
const *)
612 ioprintf(cout
, "%s %s (%s)\n", PACKAGE
, PACKAGE_VERSION
, COMMON_ARCH
);
613 if (_config
->FindB("version") == true)
617 _("Usage: apt-ftparchive [options] command\n"
618 "Commands: packages binarypath [overridefile [pathprefix]]\n"
619 " sources srcpath [overridefile [pathprefix]]\n"
622 " generate config [groups]\n"
625 "apt-ftparchive generates index files for Debian archives. It supports\n"
626 "many styles of generation from fully automated to functional replacements\n"
627 "for dpkg-scanpackages and dpkg-scansources\n"
629 "apt-ftparchive generates Package files from a tree of .debs. The\n"
630 "Package file contains the contents of all the control fields from\n"
631 "each package as well as the MD5 hash and filesize. An override file\n"
632 "is supported to force the value of Priority and Section.\n"
634 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
635 "The --source-override option can be used to specify a src override file\n"
637 "The 'packages' and 'sources' command should be run in the root of the\n"
638 "tree. BinaryPath should point to the base of the recursive search and \n"
639 "override file should contain the override flags. Pathprefix is\n"
640 "appended to the filename fields if present. Example usage from the \n"
642 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
643 " dists/potato/main/binary-i386/Packages\n"
646 " -h This help text\n"
647 " --md5 Control MD5 generation\n"
648 " -s=? Source override file\n"
650 " -d=? Select the optional caching database\n"
651 " --no-delink Enable delinking debug mode\n"
652 " --contents Control contents file generation\n"
653 " -c=? Read this configuration file\n"
654 " -o=? Set an arbitrary configuration option") << endl
;
659 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
660 // ---------------------------------------------------------------------
661 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
662 static bool SimpleGenPackages(CommandLine
&CmdL
)
664 if (CmdL
.FileSize() < 2)
665 return ShowHelp(CmdL
, nullptr);
668 if (CmdL
.FileSize() >= 3)
669 Override
= CmdL
.FileList
[2];
671 // Create a package writer object.
672 PackagesWriter
Packages(NULL
, NULL
, _config
->Find("APT::FTPArchive::DB"),
673 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
674 if (_error
->PendingError() == true)
677 if (CmdL
.FileSize() >= 4)
678 Packages
.PathPrefix
= CmdL
.FileList
[3];
680 // Do recursive directory searching
681 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
684 // Give some stats if asked for
685 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
686 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
691 // SimpleGenContents - Generate a Contents listing /*{{{*/
692 // ---------------------------------------------------------------------
694 static bool SimpleGenContents(CommandLine
&CmdL
)
696 if (CmdL
.FileSize() < 2)
697 return ShowHelp(CmdL
, nullptr);
699 // Create a package writer object.
700 ContentsWriter
Contents(NULL
, _config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
701 if (_error
->PendingError() == true)
704 // Do recursive directory searching
705 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
713 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
714 // ---------------------------------------------------------------------
715 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
716 static bool SimpleGenSources(CommandLine
&CmdL
)
718 if (CmdL
.FileSize() < 2)
719 return ShowHelp(CmdL
, nullptr);
722 if (CmdL
.FileSize() >= 3)
723 Override
= CmdL
.FileList
[2];
726 if (Override
.empty() == false)
727 SOverride
= Override
+ ".src";
729 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
732 // Create a package writer object.
733 SourcesWriter
Sources(NULL
, _config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
734 if (_error
->PendingError() == true)
737 if (CmdL
.FileSize() >= 4)
738 Sources
.PathPrefix
= CmdL
.FileList
[3];
740 // Do recursive directory searching
741 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
744 // Give some stats if asked for
745 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
746 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
751 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
752 // ---------------------------------------------------------------------
753 static bool SimpleGenRelease(CommandLine
&CmdL
)
755 if (CmdL
.FileSize() < 2)
756 return ShowHelp(CmdL
, nullptr);
758 string Dir
= CmdL
.FileList
[1];
760 ReleaseWriter
Release(NULL
, "");
761 Release
.DirStrip
= Dir
;
763 if (_error
->PendingError() == true)
766 if (Release
.RecursiveScan(Dir
) == false)
775 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
776 // ---------------------------------------------------------------------
777 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
778 vector
<PackageMap
> &PkgList
,
779 struct CacheDB::Stats
&SrcStats
,
780 struct CacheDB::Stats
&Stats
,
783 if (CmdL
.FileSize() <= 2)
785 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
786 if (I
->GenPackages(Setup
,Stats
) == false)
787 _error
->DumpErrors();
788 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
789 if (I
->GenSources(Setup
,SrcStats
) == false)
790 _error
->DumpErrors();
794 // Make a choice list out of the package list..
795 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
796 RxChoiceList
*End
= List
;
797 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
799 End
->UserData
= &(*I
);
800 End
->Str
= I
->BaseDir
.c_str();
803 End
->UserData
= &(*I
);
804 End
->Str
= I
->Tag
.c_str();
810 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
813 return _error
->Error(_("No selections matched"));
815 _error
->DumpErrors();
817 // Do the generation for Packages
818 for (End
= List
; End
->Str
!= 0; End
++)
820 if (End
->Hit
== false)
823 PackageMap
*I
= (PackageMap
*)End
->UserData
;
824 if (I
->PkgDone
== true)
826 if (I
->GenPackages(Setup
,Stats
) == false)
827 _error
->DumpErrors();
830 // Do the generation for Sources
831 for (End
= List
; End
->Str
!= 0; End
++)
833 if (End
->Hit
== false)
836 PackageMap
*I
= (PackageMap
*)End
->UserData
;
837 if (I
->SrcDone
== true)
839 if (I
->GenSources(Setup
,SrcStats
) == false)
840 _error
->DumpErrors();
849 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
850 // ---------------------------------------------------------------------
851 static bool DoGenerateContents(Configuration
&Setup
,
852 vector
<PackageMap
> &PkgList
,
855 c1out
<< "Packages done, Starting contents." << endl
;
857 // Sort the contents file list by date
858 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
859 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
862 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
863 I
->CntCompress
,A
) == false)
864 time(&I
->ContentsMTime
);
866 I
->ContentsMTime
= A
.st_mtime
;
868 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
870 /* Now for Contents.. The process here is to do a make-like dependency
871 check. Each contents file is verified to be newer than the package files
872 that describe the debs it indexes. Since the package files contain
873 hashes of the .debs this means they have not changed either so the
874 contents must be up to date. */
875 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",
876 std::numeric_limits
<unsigned int>::max())*1024;
877 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
879 // This record is not relevant
880 if (I
->ContentsDone
== true ||
881 I
->Contents
.empty() == true)
884 // Do not do everything if the user specified sections.
885 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
889 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
891 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
893 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
897 if (A
.st_mtime
> B
.st_mtime
)
901 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
902 MaxContentsChange
) == false)
903 _error
->DumpErrors();
906 if (MaxContentsChange
== 0)
908 c1out
<< "Hit contents update byte limit" << endl
;
917 // Generate - Full generate, using a config file /*{{{*/
918 // ---------------------------------------------------------------------
920 static bool Generate(CommandLine
&CmdL
)
922 struct CacheDB::Stats SrcStats
;
923 if (CmdL
.FileSize() < 2)
924 return ShowHelp(CmdL
, nullptr);
926 struct timeval StartTime
;
927 gettimeofday(&StartTime
,0);
928 struct CacheDB::Stats Stats
;
930 // Read the configuration file.
932 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
935 vector
<PackageMap
> PkgList
;
936 std::vector
<TranslationWriter
*> TransList
;
937 LoadTree(PkgList
, TransList
, Setup
);
938 LoadBinDir(PkgList
,Setup
);
940 // Sort by cache DB to improve IO locality.
941 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
942 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
945 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
947 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
949 UnloadTree(TransList
);
953 c1out
<< "Skipping Packages/Sources generation" << endl
;
956 // do Contents if needed
957 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
958 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
960 UnloadTree(TransList
);
964 struct timeval NewTime
;
965 gettimeofday(&NewTime
,0);
966 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
967 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
968 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
969 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
971 UnloadTree(TransList
);
976 // Clean - Clean out the databases /*{{{*/
977 // ---------------------------------------------------------------------
979 static bool Clean(CommandLine
&CmdL
)
981 if (CmdL
.FileSize() != 2)
982 return ShowHelp(CmdL
, nullptr);
984 // Read the configuration file.
986 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
988 // we don't need translation creation here
989 Setup
.Set("TreeDefault::Translation", "/dev/null");
991 vector
<PackageMap
> PkgList
;
992 std::vector
<TranslationWriter
*> TransList
;
993 LoadTree(PkgList
, TransList
, Setup
);
994 LoadBinDir(PkgList
,Setup
);
996 // Sort by cache DB to improve IO locality.
997 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
998 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
1000 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1002 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1004 if(I
->BinCacheDB
!= "")
1005 c0out
<< I
->BinCacheDB
<< endl
;
1006 if(I
->SrcCacheDB
!= "")
1007 c0out
<< I
->SrcCacheDB
<< endl
;
1008 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1009 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1010 if (DB
.Clean() == false)
1011 _error
->DumpErrors();
1012 if (DB_SRC
.Clean() == false)
1013 _error
->DumpErrors();
1015 string CacheDB
= I
->BinCacheDB
;
1016 string SrcCacheDB
= I
->SrcCacheDB
;
1017 while(I
!= PkgList
.end() &&
1018 I
->BinCacheDB
== CacheDB
&&
1019 I
->SrcCacheDB
== SrcCacheDB
)
1028 int main(int argc
, const char *argv
[])
1032 CommandLine::DispatchWithHelp Cmds
[] = {
1033 {"packages",&SimpleGenPackages
, nullptr},
1034 {"contents",&SimpleGenContents
, nullptr},
1035 {"sources",&SimpleGenSources
, nullptr},
1036 {"release",&SimpleGenRelease
, nullptr},
1037 {"generate",&Generate
, nullptr},
1038 {"clean",&Clean
, nullptr},
1039 {nullptr, nullptr, nullptr}
1042 // Parse the command line and initialize the package library
1044 ParseCommandLine(CmdL
, Cmds
, "apt-ftparchive", &_config
, NULL
, argc
, argv
, ShowHelp
);
1046 _config
->CndSet("quiet",0);
1047 Quiet
= _config
->FindI("quiet",0);
1048 InitOutput(clog
.rdbuf());
1050 return DispatchCommandLine(CmdL
, Cmds
);