]>
git.saurik.com Git - apt.git/blob - ftparchive/apt-ftparchive.cc
69b936dff1f28ddbd4858ab53832afacaa76789d
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 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
182 flCombine(OverrideDir
,BinOverride
),
183 flCombine(OverrideDir
,ExtraOverride
),
185 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
186 return _error
->Error(_("Package extension list is too long"));
187 if (_error
->PendingError() == true)
188 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
190 Packages
.PathPrefix
= PathPrefix
;
191 Packages
.DirStrip
= ArchiveDir
;
192 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
194 Packages
.TransWriter
= TransWriter
;
195 Packages
.LongDescription
= LongDesc
;
197 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
198 Packages
.DeLinkLimit
= DeLinkLimit
;
200 // Create a compressor object
201 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
202 PkgCompress
,Permissions
);
203 Packages
.Output
= Comp
.Input
;
204 if (_error
->PendingError() == true)
205 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
207 c0out
<< ' ' << BaseDir
<< ":" << flush
;
209 // Do recursive directory searching
210 if (FLFile
.empty() == true)
212 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
217 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
221 Packages
.Output
= 0; // Just in case
223 // Finish compressing
224 unsigned long long Size
;
225 if (Comp
.Finalize(Size
) == false)
228 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
233 << SizeToStr(Size
) << "B ";
237 struct timeval NewTime
;
238 gettimeofday(&NewTime
,0);
239 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
240 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
242 c0out
<< Packages
.Stats
.Packages
<< " files " <<
243 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
244 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
245 TimeToStr((long)Delta
) << endl
;
247 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
248 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
250 Stats
.Add(Packages
.Stats
);
251 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
253 return !_error
->PendingError();
257 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
258 // ---------------------------------------------------------------------
259 /* This generates the Sources File described by this object. */
260 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
262 if (SrcFile
.empty() == true)
265 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
266 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
267 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
269 struct timeval StartTime
;
270 gettimeofday(&StartTime
,0);
274 // Create a package writer object.
275 SourcesWriter
Sources(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 // Create a compressor object
292 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
293 SrcCompress
,Permissions
);
294 Sources
.Output
= Comp
.Input
;
295 if (_error
->PendingError() == true)
296 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
298 c0out
<< ' ' << BaseDir
<< ":" << flush
;
300 // Do recursive directory searching
301 if (FLFile
.empty() == true)
303 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
308 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
311 Sources
.Output
= 0; // Just in case
313 // Finish compressing
314 unsigned long long Size
;
315 if (Comp
.Finalize(Size
) == false)
318 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
323 << SizeToStr(Size
) << "B ";
327 struct timeval NewTime
;
328 gettimeofday(&NewTime
,0);
329 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
330 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
332 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
333 TimeToStr((long)Delta
) << endl
;
335 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
336 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
338 Stats
.Add(Sources
.Stats
);
339 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
341 return !_error
->PendingError();
344 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
345 // ---------------------------------------------------------------------
346 /* This generates the contents file partially described by this object.
347 It searches the given iterator range for other package files that map
348 into this contents file and includes their data as well when building. */
349 bool PackageMap::GenContents(Configuration
&Setup
,
350 vector
<PackageMap
>::iterator Begin
,
351 vector
<PackageMap
>::iterator End
,
354 if (Contents
.empty() == true)
360 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
361 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
362 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
364 struct timeval StartTime
;
365 gettimeofday(&StartTime
,0);
367 // Create a package writer object.
368 ContentsWriter
Contents("", Arch
);
369 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
370 return _error
->Error(_("Package extension list is too long"));
371 if (_error
->PendingError() == true)
374 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
375 CntCompress
,Permissions
);
376 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
377 Contents
.Output
= Comp
.Input
;
378 if (_error
->PendingError() == true)
381 // Write the header out.
382 if (ContentsHead
.empty() == false)
384 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
385 if (_error
->PendingError() == true)
388 unsigned long long Size
= Head
.Size();
389 unsigned char Buf
[4096];
392 unsigned long long ToRead
= Size
;
393 if (Size
> sizeof(Buf
))
394 ToRead
= sizeof(Buf
);
396 if (Head
.Read(Buf
,ToRead
) == false)
399 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
400 return _error
->Errno("fwrite",_("Error writing header to contents file"));
406 /* Go over all the package file records and parse all the package
407 files associated with this contents file into one great big honking
408 memory structure, then dump the sorted version */
409 c0out
<< ' ' << this->Contents
<< ":" << flush
;
410 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
412 if (I
->Contents
!= this->Contents
)
415 Contents
.Prefix
= ArchiveDir
;
416 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
417 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
420 I
->ContentsDone
= true;
425 // Finish compressing
426 unsigned long long Size
;
427 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
430 return _error
->Error(_("Error processing contents %s"),
431 this->Contents
.c_str());
436 c0out
<< " New " << SizeToStr(Size
) << "B ";
445 struct timeval NewTime
;
446 gettimeofday(&NewTime
,0);
447 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
448 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
450 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
451 c0out
<< " Misses in Cache: " << Contents
.Stats
.Misses
<< endl
;
453 c0out
<< Contents
.Stats
.Packages
<< " files " <<
454 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
455 TimeToStr((long)Delta
) << endl
;
461 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
462 // ---------------------------------------------------------------------
463 /* This populates the PkgList with all the possible permutations of the
464 section/arch lists. */
465 static void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
468 string DDir
= Setup
.Find("TreeDefault::Directory",
469 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
470 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
471 "$(DIST)/$(SECTION)/source/");
472 string DPkg
= Setup
.Find("TreeDefault::Packages",
473 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
474 string DTrans
= Setup
.Find("TreeDefault::Translation",
475 "$(DIST)/$(SECTION)/i18n/Translation-en");
476 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
477 "$(DIST)/$(SECTION)/");
478 string DContents
= Setup
.Find("TreeDefault::Contents",
479 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
480 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
481 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
482 "packages-$(ARCH).db");
483 string SrcDBCache
= Setup
.Find("TreeDefault::SrcCacheDB",
484 "sources-$(SECTION).db");
485 string DSources
= Setup
.Find("TreeDefault::Sources",
486 "$(DIST)/$(SECTION)/source/Sources");
487 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
488 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
490 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
492 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
493 _config
->FindB("APT::FTPArchive::LongDescription", true));
494 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
496 // Process 'tree' type sections
497 const Configuration::Item
*Top
= Setup
.Tree("tree");
498 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
500 Configuration
Block(Top
);
501 string Dist
= Top
->Tag
;
503 // Parse the sections
504 string Tmp
= Block
.Find("Sections");
505 const char *Sections
= Tmp
.c_str();
507 while (ParseQuoteWord(Sections
,Section
) == true)
510 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
511 {"$(SECTION)",&Section
},
514 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
515 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
516 TranslationWriter
*TransWriter
;
517 if (DTrans
.empty() == false && LongDesc
== false)
519 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
520 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
521 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
522 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
527 string
const Tmp2
= Block
.Find("Architectures");
528 const char *Archs
= Tmp2
.c_str();
529 while (ParseQuoteWord(Archs
,Arch
) == true)
532 Itm
.Permissions
= Perms
;
533 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
534 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
536 if (stringcasecmp(Arch
,"source") == 0)
538 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
539 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
540 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
541 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
542 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
543 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
544 Itm
.SrcCacheDB
= SubstVar(Block
.Find("SrcCacheDB",SrcDBCache
.c_str()),Vars
);
548 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
549 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
550 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
551 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
553 Itm
.LongDesc
= LongDesc
;
554 if (TransWriter
!= NULL
)
556 TransWriter
->IncreaseRefCounter();
557 Itm
.TransWriter
= TransWriter
;
559 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
560 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
561 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
562 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
565 Itm
.GetGeneral(Setup
,Block
);
566 PkgList
.push_back(Itm
);
568 // we didn't use this TransWriter, so we can release it
569 if (TransWriter
!= NULL
&& TransWriter
->GetRefCounter() == 0)
577 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
578 // ---------------------------------------------------------------------
580 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
582 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
584 // Process 'bindirectory' type sections
585 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
586 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
588 Configuration
Block(Top
);
591 Itm
.PkgFile
= Block
.Find("Packages");
592 Itm
.SrcFile
= Block
.Find("Sources");
593 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
594 Itm
.SrcCacheDB
= Block
.Find("SrcCacheDB");
595 Itm
.BinOverride
= Block
.Find("BinOverride");
596 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
597 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
598 Itm
.SrcOverride
= Block
.Find("SrcOverride");
599 Itm
.BaseDir
= Top
->Tag
;
600 Itm
.FLFile
= Block
.Find("FileList");
601 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
602 Itm
.Contents
= Block
.Find("Contents");
603 Itm
.ContentsHead
= Block
.Find("Contents::Header");
604 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
606 Itm
.GetGeneral(Setup
,Block
);
607 PkgList
.push_back(Itm
);
614 // ShowHelp - Show the help text /*{{{*/
615 // ---------------------------------------------------------------------
617 static bool ShowHelp(CommandLine
&)
619 ioprintf(cout
, "%s %s (%s)\n", PACKAGE
, PACKAGE_VERSION
, COMMON_ARCH
);
620 if (_config
->FindB("version") == true)
624 _("Usage: apt-ftparchive [options] command\n"
625 "Commands: packages binarypath [overridefile [pathprefix]]\n"
626 " sources srcpath [overridefile [pathprefix]]\n"
629 " generate config [groups]\n"
632 "apt-ftparchive generates index files for Debian archives. It supports\n"
633 "many styles of generation from fully automated to functional replacements\n"
634 "for dpkg-scanpackages and dpkg-scansources\n"
636 "apt-ftparchive generates Package files from a tree of .debs. The\n"
637 "Package file contains the contents of all the control fields from\n"
638 "each package as well as the MD5 hash and filesize. An override file\n"
639 "is supported to force the value of Priority and Section.\n"
641 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
642 "The --source-override option can be used to specify a src override file\n"
644 "The 'packages' and 'sources' command should be run in the root of the\n"
645 "tree. BinaryPath should point to the base of the recursive search and \n"
646 "override file should contain the override flags. Pathprefix is\n"
647 "appended to the filename fields if present. Example usage from the \n"
649 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
650 " dists/potato/main/binary-i386/Packages\n"
653 " -h This help text\n"
654 " --md5 Control MD5 generation\n"
655 " -s=? Source override file\n"
657 " -d=? Select the optional caching database\n"
658 " --no-delink Enable delinking debug mode\n"
659 " --contents Control contents file generation\n"
660 " -c=? Read this configuration file\n"
661 " -o=? Set an arbitrary configuration option") << endl
;
666 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
667 // ---------------------------------------------------------------------
668 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
669 static bool SimpleGenPackages(CommandLine
&CmdL
)
671 if (CmdL
.FileSize() < 2)
672 return ShowHelp(CmdL
);
675 if (CmdL
.FileSize() >= 3)
676 Override
= CmdL
.FileList
[2];
678 // Create a package writer object.
679 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
680 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
681 if (_error
->PendingError() == true)
684 if (CmdL
.FileSize() >= 4)
685 Packages
.PathPrefix
= CmdL
.FileList
[3];
687 // Do recursive directory searching
688 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
691 // Give some stats if asked for
692 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
693 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
698 // SimpleGenContents - Generate a Contents listing /*{{{*/
699 // ---------------------------------------------------------------------
701 static bool SimpleGenContents(CommandLine
&CmdL
)
703 if (CmdL
.FileSize() < 2)
704 return ShowHelp(CmdL
);
706 // Create a package writer object.
707 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
708 if (_error
->PendingError() == true)
711 // Do recursive directory searching
712 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
720 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
721 // ---------------------------------------------------------------------
722 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
723 static bool SimpleGenSources(CommandLine
&CmdL
)
725 if (CmdL
.FileSize() < 2)
726 return ShowHelp(CmdL
);
729 if (CmdL
.FileSize() >= 3)
730 Override
= CmdL
.FileList
[2];
733 if (Override
.empty() == false)
734 SOverride
= Override
+ ".src";
736 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
739 // Create a package writer object.
740 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
741 if (_error
->PendingError() == true)
744 if (CmdL
.FileSize() >= 4)
745 Sources
.PathPrefix
= CmdL
.FileList
[3];
747 // Do recursive directory searching
748 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
751 // Give some stats if asked for
752 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
753 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
758 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
759 // ---------------------------------------------------------------------
760 static bool SimpleGenRelease(CommandLine
&CmdL
)
762 if (CmdL
.FileSize() < 2)
763 return ShowHelp(CmdL
);
765 string Dir
= CmdL
.FileList
[1];
767 ReleaseWriter
Release("");
768 Release
.DirStrip
= Dir
;
770 if (_error
->PendingError() == true)
773 if (Release
.RecursiveScan(Dir
) == false)
782 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
783 // ---------------------------------------------------------------------
784 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
785 vector
<PackageMap
> &PkgList
,
786 struct CacheDB::Stats
&SrcStats
,
787 struct CacheDB::Stats
&Stats
,
790 if (CmdL
.FileSize() <= 2)
792 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
793 if (I
->GenPackages(Setup
,Stats
) == false)
794 _error
->DumpErrors();
795 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
796 if (I
->GenSources(Setup
,SrcStats
) == false)
797 _error
->DumpErrors();
801 // Make a choice list out of the package list..
802 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
803 RxChoiceList
*End
= List
;
804 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
806 End
->UserData
= &(*I
);
807 End
->Str
= I
->BaseDir
.c_str();
810 End
->UserData
= &(*I
);
811 End
->Str
= I
->Tag
.c_str();
817 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
820 return _error
->Error(_("No selections matched"));
822 _error
->DumpErrors();
824 // Do the generation for Packages
825 for (End
= List
; End
->Str
!= 0; End
++)
827 if (End
->Hit
== false)
830 PackageMap
*I
= (PackageMap
*)End
->UserData
;
831 if (I
->PkgDone
== true)
833 if (I
->GenPackages(Setup
,Stats
) == false)
834 _error
->DumpErrors();
837 // Do the generation for Sources
838 for (End
= List
; End
->Str
!= 0; End
++)
840 if (End
->Hit
== false)
843 PackageMap
*I
= (PackageMap
*)End
->UserData
;
844 if (I
->SrcDone
== true)
846 if (I
->GenSources(Setup
,SrcStats
) == false)
847 _error
->DumpErrors();
853 // close the Translation master files
854 for (vector
<PackageMap
>::reverse_iterator I
= PkgList
.rbegin(); I
!= PkgList
.rend(); ++I
)
855 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
856 delete I
->TransWriter
;
862 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
863 // ---------------------------------------------------------------------
864 static bool DoGenerateContents(Configuration
&Setup
,
865 vector
<PackageMap
> &PkgList
,
868 c1out
<< "Packages done, Starting contents." << endl
;
870 // Sort the contents file list by date
871 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
872 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
875 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
876 I
->CntCompress
,A
) == false)
877 time(&I
->ContentsMTime
);
879 I
->ContentsMTime
= A
.st_mtime
;
881 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
883 /* Now for Contents.. The process here is to do a make-like dependency
884 check. Each contents file is verified to be newer than the package files
885 that describe the debs it indexes. Since the package files contain
886 hashes of the .debs this means they have not changed either so the
887 contents must be up to date. */
888 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
889 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
891 // This record is not relevant
892 if (I
->ContentsDone
== true ||
893 I
->Contents
.empty() == true)
896 // Do not do everything if the user specified sections.
897 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
901 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
903 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
905 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
909 if (A
.st_mtime
> B
.st_mtime
)
913 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
914 MaxContentsChange
) == false)
915 _error
->DumpErrors();
918 if (MaxContentsChange
== 0)
920 c1out
<< "Hit contents update byte limit" << endl
;
929 // Generate - Full generate, using a config file /*{{{*/
930 // ---------------------------------------------------------------------
932 static bool Generate(CommandLine
&CmdL
)
934 struct CacheDB::Stats SrcStats
;
935 if (CmdL
.FileSize() < 2)
936 return ShowHelp(CmdL
);
938 struct timeval StartTime
;
939 gettimeofday(&StartTime
,0);
940 struct CacheDB::Stats Stats
;
942 // Read the configuration file.
944 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
947 vector
<PackageMap
> PkgList
;
948 LoadTree(PkgList
,Setup
);
949 LoadBinDir(PkgList
,Setup
);
951 // Sort by cache DB to improve IO locality.
952 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
953 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
956 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
958 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
961 c1out
<< "Skipping Packages/Sources generation" << endl
;
964 // do Contents if needed
965 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
966 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
969 struct timeval NewTime
;
970 gettimeofday(&NewTime
,0);
971 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
972 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
973 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
974 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
980 // Clean - Clean out the databases /*{{{*/
981 // ---------------------------------------------------------------------
983 static bool Clean(CommandLine
&CmdL
)
985 if (CmdL
.FileSize() != 2)
986 return ShowHelp(CmdL
);
988 // Read the configuration file.
990 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
993 vector
<PackageMap
> PkgList
;
994 LoadTree(PkgList
,Setup
);
995 LoadBinDir(PkgList
,Setup
);
997 // Sort by cache DB to improve IO locality.
998 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
999 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
1001 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1003 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1005 if(I
->BinCacheDB
!= "")
1006 c0out
<< I
->BinCacheDB
<< endl
;
1007 if(I
->SrcCacheDB
!= "")
1008 c0out
<< I
->SrcCacheDB
<< endl
;
1009 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1010 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1011 if (DB
.Clean() == false)
1012 _error
->DumpErrors();
1013 if (DB_SRC
.Clean() == false)
1014 _error
->DumpErrors();
1016 string CacheDB
= I
->BinCacheDB
;
1017 string SrcCacheDB
= I
->SrcCacheDB
;
1018 while(I
!= PkgList
.end() &&
1019 I
->BinCacheDB
== CacheDB
&&
1020 I
->SrcCacheDB
== SrcCacheDB
)
1030 int main(int argc
, const char *argv
[])
1032 setlocale(LC_ALL
, "");
1033 CommandLine::Args Args
[] = {
1034 {'h',"help","help",0},
1035 {0,"md5","APT::FTPArchive::MD5",0},
1036 {0,"sha1","APT::FTPArchive::SHA1",0},
1037 {0,"sha256","APT::FTPArchive::SHA256",0},
1038 {'v',"version","version",0},
1039 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
1040 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
1041 {'q',"quiet","quiet",CommandLine::IntLevel
},
1042 {'q',"silent","quiet",CommandLine::IntLevel
},
1043 {0,"delink","APT::FTPArchive::DeLinkAct",0},
1044 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
1045 {0,"contents","APT::FTPArchive::Contents",0},
1046 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
1047 {'c',"config-file",0,CommandLine::ConfigFile
},
1048 {'o',"option",0,CommandLine::ArbItem
},
1050 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
1051 {"contents",&SimpleGenContents
},
1052 {"sources",&SimpleGenSources
},
1053 {"release",&SimpleGenRelease
},
1054 {"generate",&Generate
},
1059 // Parse the command line and initialize the package library
1060 CommandLine
CmdL(Args
,_config
);
1061 ParseCommandLine(CmdL
, Cmds
, Args
, &_config
, NULL
, argc
, argv
, ShowHelp
);
1063 _config
->CndSet("quiet",0);
1064 Quiet
= _config
->FindI("quiet",0);
1065 InitOutput(clog
.rdbuf());
1067 // Match the operation
1068 CmdL
.DispatchArg(Cmds
);
1070 if (_error
->empty() == false)
1072 bool Errors
= _error
->PendingError();
1073 _error
->DumpErrors();
1074 return Errors
== true?100:0;