]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-ftparchive.cc,v 1.8.2.3 2004/01/02 22:01:48 mdz Exp $
4 /* ######################################################################
6 apt-ftparchive - Efficient work-alike for dpkg-scanpackages
8 Let contents be disabled from the conf
10 ##################################################################### */
12 // Include Files /*{{{*/
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/configuration.h>
17 #include <apt-pkg/cmndline.h>
18 #include <apt-pkg/strutl.h>
19 #include <apt-pkg/init.h>
20 #include <apt-pkg/fileutl.h>
22 #include <apt-private/private-cmndline.h>
23 #include <apt-private/private-output.h>
39 #include "apt-ftparchive.h"
40 #include "multicompress.h"
49 // struct PackageMap - List of all package files in the config file /*{{{*/
50 // ---------------------------------------------------------------------
56 string InternalPrefix
;
61 // Stuff for the Package File
68 // We generate for this given arch
71 // Stuff for the Source File
74 string SrcExtraOverride
;
76 // Translation master file
78 TranslationWriter
*TransWriter
;
90 unsigned int DeLinkLimit
;
98 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
100 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
101 {return x
.ContentsMTime
< y
.ContentsMTime
;};
104 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
106 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
107 {return x
.BinCacheDB
< y
.BinCacheDB
;};
110 struct SrcDBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
112 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
113 {return x
.SrcCacheDB
< y
.SrcCacheDB
;};
116 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
117 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
118 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
119 bool GenContents(Configuration
&Setup
,
120 vector
<PackageMap
>::iterator Begin
,
121 vector
<PackageMap
>::iterator End
,
122 unsigned long &Left
);
124 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
125 ContentsDone(false), PkgDone(false), SrcDone(false),
130 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
131 // ---------------------------------------------------------------------
133 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
135 PathPrefix
= Block
.Find("PathPrefix");
137 if (Block
.FindB("External-Links",true) == false)
138 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
142 PkgCompress
= Block
.Find("Packages::Compress",
143 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
144 CntCompress
= Block
.Find("Contents::Compress",
145 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
146 SrcCompress
= Block
.Find("Sources::Compress",
147 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
149 SrcExt
= Block
.Find("Sources::Extensions",
150 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
151 PkgExt
= Block
.Find("Packages::Extensions",
152 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
154 Permissions
= Setup
.FindI("Default::FileMode",0644);
156 if (FLFile
.empty() == false)
157 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
163 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
164 // ---------------------------------------------------------------------
165 /* This generates the Package File described by this object. */
166 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
168 if (PkgFile
.empty() == true)
171 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
172 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
173 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
175 struct timeval StartTime
;
176 gettimeofday(&StartTime
,0);
180 // Create a package writer object.
181 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
182 PkgCompress
,Permissions
);
183 PackagesWriter
Packages(&Comp
.Input
, flCombine(CacheDir
,BinCacheDB
),
184 flCombine(OverrideDir
,BinOverride
),
185 flCombine(OverrideDir
,ExtraOverride
),
187 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
188 return _error
->Error(_("Package extension list is too long"));
189 if (_error
->PendingError() == true)
190 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
192 Packages
.PathPrefix
= PathPrefix
;
193 Packages
.DirStrip
= ArchiveDir
;
194 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
196 Packages
.TransWriter
= TransWriter
;
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
,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
;
512 if (DTrans
.empty() == false && LongDesc
== false)
514 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
515 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
516 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
517 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
522 string
const Tmp2
= Block
.Find("Architectures");
523 const char *Archs
= Tmp2
.c_str();
524 while (ParseQuoteWord(Archs
,Arch
) == true)
527 Itm
.Permissions
= Perms
;
528 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
529 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
531 if (stringcasecmp(Arch
,"source") == 0)
533 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
534 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
535 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
536 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
537 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
538 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
539 Itm
.SrcCacheDB
= SubstVar(Block
.Find("SrcCacheDB",SrcDBCache
.c_str()),Vars
);
543 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
544 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
545 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
546 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
548 Itm
.LongDesc
= LongDesc
;
549 if (TransWriter
!= NULL
)
551 TransWriter
->IncreaseRefCounter();
552 Itm
.TransWriter
= TransWriter
;
554 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
555 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
556 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
557 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
560 Itm
.GetGeneral(Setup
,Block
);
561 PkgList
.push_back(Itm
);
563 // we didn't use this TransWriter, so we can release it
564 if (TransWriter
!= NULL
&& TransWriter
->GetRefCounter() == 0)
572 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
573 // ---------------------------------------------------------------------
575 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
577 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
579 // Process 'bindirectory' type sections
580 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
581 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
583 Configuration
Block(Top
);
586 Itm
.PkgFile
= Block
.Find("Packages");
587 Itm
.SrcFile
= Block
.Find("Sources");
588 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
589 Itm
.SrcCacheDB
= Block
.Find("SrcCacheDB");
590 Itm
.BinOverride
= Block
.Find("BinOverride");
591 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
592 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
593 Itm
.SrcOverride
= Block
.Find("SrcOverride");
594 Itm
.BaseDir
= Top
->Tag
;
595 Itm
.FLFile
= Block
.Find("FileList");
596 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
597 Itm
.Contents
= Block
.Find("Contents");
598 Itm
.ContentsHead
= Block
.Find("Contents::Header");
599 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
601 Itm
.GetGeneral(Setup
,Block
);
602 PkgList
.push_back(Itm
);
609 // ShowHelp - Show the help text /*{{{*/
610 // ---------------------------------------------------------------------
612 static bool ShowHelp(CommandLine
&)
614 ioprintf(cout
, "%s %s (%s)\n", PACKAGE
, PACKAGE_VERSION
, COMMON_ARCH
);
615 if (_config
->FindB("version") == true)
619 _("Usage: apt-ftparchive [options] command\n"
620 "Commands: packages binarypath [overridefile [pathprefix]]\n"
621 " sources srcpath [overridefile [pathprefix]]\n"
624 " generate config [groups]\n"
627 "apt-ftparchive generates index files for Debian archives. It supports\n"
628 "many styles of generation from fully automated to functional replacements\n"
629 "for dpkg-scanpackages and dpkg-scansources\n"
631 "apt-ftparchive generates Package files from a tree of .debs. The\n"
632 "Package file contains the contents of all the control fields from\n"
633 "each package as well as the MD5 hash and filesize. An override file\n"
634 "is supported to force the value of Priority and Section.\n"
636 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
637 "The --source-override option can be used to specify a src override file\n"
639 "The 'packages' and 'sources' command should be run in the root of the\n"
640 "tree. BinaryPath should point to the base of the recursive search and \n"
641 "override file should contain the override flags. Pathprefix is\n"
642 "appended to the filename fields if present. Example usage from the \n"
644 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
645 " dists/potato/main/binary-i386/Packages\n"
648 " -h This help text\n"
649 " --md5 Control MD5 generation\n"
650 " -s=? Source override file\n"
652 " -d=? Select the optional caching database\n"
653 " --no-delink Enable delinking debug mode\n"
654 " --contents Control contents file generation\n"
655 " -c=? Read this configuration file\n"
656 " -o=? Set an arbitrary configuration option") << endl
;
661 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
662 // ---------------------------------------------------------------------
663 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
664 static bool SimpleGenPackages(CommandLine
&CmdL
)
666 if (CmdL
.FileSize() < 2)
667 return ShowHelp(CmdL
);
670 if (CmdL
.FileSize() >= 3)
671 Override
= CmdL
.FileList
[2];
673 // Create a package writer object.
674 PackagesWriter
Packages(NULL
, _config
->Find("APT::FTPArchive::DB"),
675 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
676 if (_error
->PendingError() == true)
679 if (CmdL
.FileSize() >= 4)
680 Packages
.PathPrefix
= CmdL
.FileList
[3];
682 // Do recursive directory searching
683 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
686 // Give some stats if asked for
687 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
688 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
693 // SimpleGenContents - Generate a Contents listing /*{{{*/
694 // ---------------------------------------------------------------------
696 static bool SimpleGenContents(CommandLine
&CmdL
)
698 if (CmdL
.FileSize() < 2)
699 return ShowHelp(CmdL
);
701 // Create a package writer object.
702 ContentsWriter
Contents(NULL
, _config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
703 if (_error
->PendingError() == true)
706 // Do recursive directory searching
707 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
715 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
716 // ---------------------------------------------------------------------
717 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
718 static bool SimpleGenSources(CommandLine
&CmdL
)
720 if (CmdL
.FileSize() < 2)
721 return ShowHelp(CmdL
);
724 if (CmdL
.FileSize() >= 3)
725 Override
= CmdL
.FileList
[2];
728 if (Override
.empty() == false)
729 SOverride
= Override
+ ".src";
731 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
734 // Create a package writer object.
735 SourcesWriter
Sources(NULL
, _config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
736 if (_error
->PendingError() == true)
739 if (CmdL
.FileSize() >= 4)
740 Sources
.PathPrefix
= CmdL
.FileList
[3];
742 // Do recursive directory searching
743 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
746 // Give some stats if asked for
747 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
748 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
753 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
754 // ---------------------------------------------------------------------
755 static bool SimpleGenRelease(CommandLine
&CmdL
)
757 if (CmdL
.FileSize() < 2)
758 return ShowHelp(CmdL
);
760 string Dir
= CmdL
.FileList
[1];
762 ReleaseWriter
Release(NULL
, "");
763 Release
.DirStrip
= Dir
;
765 if (_error
->PendingError() == true)
768 if (Release
.RecursiveScan(Dir
) == false)
777 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
778 // ---------------------------------------------------------------------
779 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
780 vector
<PackageMap
> &PkgList
,
781 struct CacheDB::Stats
&SrcStats
,
782 struct CacheDB::Stats
&Stats
,
785 if (CmdL
.FileSize() <= 2)
787 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
788 if (I
->GenPackages(Setup
,Stats
) == false)
789 _error
->DumpErrors();
790 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
791 if (I
->GenSources(Setup
,SrcStats
) == false)
792 _error
->DumpErrors();
796 // Make a choice list out of the package list..
797 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
798 RxChoiceList
*End
= List
;
799 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
801 End
->UserData
= &(*I
);
802 End
->Str
= I
->BaseDir
.c_str();
805 End
->UserData
= &(*I
);
806 End
->Str
= I
->Tag
.c_str();
812 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
815 return _error
->Error(_("No selections matched"));
817 _error
->DumpErrors();
819 // Do the generation for Packages
820 for (End
= List
; End
->Str
!= 0; End
++)
822 if (End
->Hit
== false)
825 PackageMap
*I
= (PackageMap
*)End
->UserData
;
826 if (I
->PkgDone
== true)
828 if (I
->GenPackages(Setup
,Stats
) == false)
829 _error
->DumpErrors();
832 // Do the generation for Sources
833 for (End
= List
; End
->Str
!= 0; End
++)
835 if (End
->Hit
== false)
838 PackageMap
*I
= (PackageMap
*)End
->UserData
;
839 if (I
->SrcDone
== true)
841 if (I
->GenSources(Setup
,SrcStats
) == false)
842 _error
->DumpErrors();
848 // close the Translation master files
849 for (vector
<PackageMap
>::reverse_iterator I
= PkgList
.rbegin(); I
!= PkgList
.rend(); ++I
)
850 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
851 delete I
->TransWriter
;
857 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
858 // ---------------------------------------------------------------------
859 static bool DoGenerateContents(Configuration
&Setup
,
860 vector
<PackageMap
> &PkgList
,
863 c1out
<< "Packages done, Starting contents." << endl
;
865 // Sort the contents file list by date
866 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
867 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
870 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
871 I
->CntCompress
,A
) == false)
872 time(&I
->ContentsMTime
);
874 I
->ContentsMTime
= A
.st_mtime
;
876 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
878 /* Now for Contents.. The process here is to do a make-like dependency
879 check. Each contents file is verified to be newer than the package files
880 that describe the debs it indexes. Since the package files contain
881 hashes of the .debs this means they have not changed either so the
882 contents must be up to date. */
883 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
884 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
886 // This record is not relevant
887 if (I
->ContentsDone
== true ||
888 I
->Contents
.empty() == true)
891 // Do not do everything if the user specified sections.
892 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
896 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
898 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
900 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
904 if (A
.st_mtime
> B
.st_mtime
)
908 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
909 MaxContentsChange
) == false)
910 _error
->DumpErrors();
913 if (MaxContentsChange
== 0)
915 c1out
<< "Hit contents update byte limit" << endl
;
924 // Generate - Full generate, using a config file /*{{{*/
925 // ---------------------------------------------------------------------
927 static bool Generate(CommandLine
&CmdL
)
929 struct CacheDB::Stats SrcStats
;
930 if (CmdL
.FileSize() < 2)
931 return ShowHelp(CmdL
);
933 struct timeval StartTime
;
934 gettimeofday(&StartTime
,0);
935 struct CacheDB::Stats Stats
;
937 // Read the configuration file.
939 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
942 vector
<PackageMap
> PkgList
;
943 LoadTree(PkgList
,Setup
);
944 LoadBinDir(PkgList
,Setup
);
946 // Sort by cache DB to improve IO locality.
947 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
948 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
951 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
953 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
956 c1out
<< "Skipping Packages/Sources generation" << endl
;
959 // do Contents if needed
960 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
961 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
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
;
975 // Clean - Clean out the databases /*{{{*/
976 // ---------------------------------------------------------------------
978 static bool Clean(CommandLine
&CmdL
)
980 if (CmdL
.FileSize() != 2)
981 return ShowHelp(CmdL
);
983 // Read the configuration file.
985 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
988 vector
<PackageMap
> PkgList
;
989 LoadTree(PkgList
,Setup
);
990 LoadBinDir(PkgList
,Setup
);
992 // Sort by cache DB to improve IO locality.
993 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
994 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
996 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
998 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1000 if(I
->BinCacheDB
!= "")
1001 c0out
<< I
->BinCacheDB
<< endl
;
1002 if(I
->SrcCacheDB
!= "")
1003 c0out
<< I
->SrcCacheDB
<< endl
;
1004 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1005 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1006 if (DB
.Clean() == false)
1007 _error
->DumpErrors();
1008 if (DB_SRC
.Clean() == false)
1009 _error
->DumpErrors();
1011 string CacheDB
= I
->BinCacheDB
;
1012 string SrcCacheDB
= I
->SrcCacheDB
;
1013 while(I
!= PkgList
.end() &&
1014 I
->BinCacheDB
== CacheDB
&&
1015 I
->SrcCacheDB
== SrcCacheDB
)
1025 int main(int argc
, const char *argv
[])
1027 setlocale(LC_ALL
, "");
1028 CommandLine::Args Args
[] = {
1029 {'h',"help","help",0},
1030 {0,"md5","APT::FTPArchive::MD5",0},
1031 {0,"sha1","APT::FTPArchive::SHA1",0},
1032 {0,"sha256","APT::FTPArchive::SHA256",0},
1033 {'v',"version","version",0},
1034 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
1035 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
1036 {'q',"quiet","quiet",CommandLine::IntLevel
},
1037 {'q',"silent","quiet",CommandLine::IntLevel
},
1038 {0,"delink","APT::FTPArchive::DeLinkAct",0},
1039 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
1040 {0,"contents","APT::FTPArchive::Contents",0},
1041 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
1042 {'c',"config-file",0,CommandLine::ConfigFile
},
1043 {'o',"option",0,CommandLine::ArbItem
},
1045 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
1046 {"contents",&SimpleGenContents
},
1047 {"sources",&SimpleGenSources
},
1048 {"release",&SimpleGenRelease
},
1049 {"generate",&Generate
},
1054 // Parse the command line and initialize the package library
1055 CommandLine
CmdL(Args
,_config
);
1056 ParseCommandLine(CmdL
, Cmds
, Args
, &_config
, NULL
, argc
, argv
, ShowHelp
);
1058 _config
->CndSet("quiet",0);
1059 Quiet
= _config
->FindI("quiet",0);
1060 InitOutput(clog
.rdbuf());
1062 // Match the operation
1063 CmdL
.DispatchArg(Cmds
);
1065 if (_error
->empty() == false)
1067 bool Errors
= _error
->PendingError();
1068 _error
->DumpErrors();
1069 return Errors
== true?100:0;