]>
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 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 for %s compiled on %s %s\n"),PACKAGE
,PACKAGE_VERSION
,
620 COMMON_ARCH
,__DATE__
,__TIME__
);
621 if (_config
->FindB("version") == true)
625 _("Usage: apt-ftparchive [options] command\n"
626 "Commands: packages binarypath [overridefile [pathprefix]]\n"
627 " sources srcpath [overridefile [pathprefix]]\n"
630 " generate config [groups]\n"
633 "apt-ftparchive generates index files for Debian archives. It supports\n"
634 "many styles of generation from fully automated to functional replacements\n"
635 "for dpkg-scanpackages and dpkg-scansources\n"
637 "apt-ftparchive generates Package files from a tree of .debs. The\n"
638 "Package file contains the contents of all the control fields from\n"
639 "each package as well as the MD5 hash and filesize. An override file\n"
640 "is supported to force the value of Priority and Section.\n"
642 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
643 "The --source-override option can be used to specify a src override file\n"
645 "The 'packages' and 'sources' command should be run in the root of the\n"
646 "tree. BinaryPath should point to the base of the recursive search and \n"
647 "override file should contain the override flags. Pathprefix is\n"
648 "appended to the filename fields if present. Example usage from the \n"
650 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
651 " dists/potato/main/binary-i386/Packages\n"
654 " -h This help text\n"
655 " --md5 Control MD5 generation\n"
656 " -s=? Source override file\n"
658 " -d=? Select the optional caching database\n"
659 " --no-delink Enable delinking debug mode\n"
660 " --contents Control contents file generation\n"
661 " -c=? Read this configuration file\n"
662 " -o=? Set an arbitrary configuration option") << endl
;
667 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
668 // ---------------------------------------------------------------------
669 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
670 static bool SimpleGenPackages(CommandLine
&CmdL
)
672 if (CmdL
.FileSize() < 2)
673 return ShowHelp(CmdL
);
676 if (CmdL
.FileSize() >= 3)
677 Override
= CmdL
.FileList
[2];
679 // Create a package writer object.
680 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
681 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
682 if (_error
->PendingError() == true)
685 if (CmdL
.FileSize() >= 4)
686 Packages
.PathPrefix
= CmdL
.FileList
[3];
688 // Do recursive directory searching
689 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
692 // Give some stats if asked for
693 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
694 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
699 // SimpleGenContents - Generate a Contents listing /*{{{*/
700 // ---------------------------------------------------------------------
702 static bool SimpleGenContents(CommandLine
&CmdL
)
704 if (CmdL
.FileSize() < 2)
705 return ShowHelp(CmdL
);
707 // Create a package writer object.
708 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
709 if (_error
->PendingError() == true)
712 // Do recursive directory searching
713 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
721 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
722 // ---------------------------------------------------------------------
723 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
724 static bool SimpleGenSources(CommandLine
&CmdL
)
726 if (CmdL
.FileSize() < 2)
727 return ShowHelp(CmdL
);
730 if (CmdL
.FileSize() >= 3)
731 Override
= CmdL
.FileList
[2];
734 if (Override
.empty() == false)
735 SOverride
= Override
+ ".src";
737 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
740 // Create a package writer object.
741 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
742 if (_error
->PendingError() == true)
745 if (CmdL
.FileSize() >= 4)
746 Sources
.PathPrefix
= CmdL
.FileList
[3];
748 // Do recursive directory searching
749 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
752 // Give some stats if asked for
753 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
754 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
759 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
760 // ---------------------------------------------------------------------
761 static bool SimpleGenRelease(CommandLine
&CmdL
)
763 if (CmdL
.FileSize() < 2)
764 return ShowHelp(CmdL
);
766 string Dir
= CmdL
.FileList
[1];
768 ReleaseWriter
Release("");
769 Release
.DirStrip
= Dir
;
771 if (_error
->PendingError() == true)
774 if (Release
.RecursiveScan(Dir
) == false)
783 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
784 // ---------------------------------------------------------------------
785 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
786 vector
<PackageMap
> &PkgList
,
787 struct CacheDB::Stats
&SrcStats
,
788 struct CacheDB::Stats
&Stats
,
791 if (CmdL
.FileSize() <= 2)
793 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
794 if (I
->GenPackages(Setup
,Stats
) == false)
795 _error
->DumpErrors();
796 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
797 if (I
->GenSources(Setup
,SrcStats
) == false)
798 _error
->DumpErrors();
802 // Make a choice list out of the package list..
803 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
804 RxChoiceList
*End
= List
;
805 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
807 End
->UserData
= &(*I
);
808 End
->Str
= I
->BaseDir
.c_str();
811 End
->UserData
= &(*I
);
812 End
->Str
= I
->Tag
.c_str();
818 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
821 return _error
->Error(_("No selections matched"));
823 _error
->DumpErrors();
825 // Do the generation for Packages
826 for (End
= List
; End
->Str
!= 0; End
++)
828 if (End
->Hit
== false)
831 PackageMap
*I
= (PackageMap
*)End
->UserData
;
832 if (I
->PkgDone
== true)
834 if (I
->GenPackages(Setup
,Stats
) == false)
835 _error
->DumpErrors();
838 // Do the generation for Sources
839 for (End
= List
; End
->Str
!= 0; End
++)
841 if (End
->Hit
== false)
844 PackageMap
*I
= (PackageMap
*)End
->UserData
;
845 if (I
->SrcDone
== true)
847 if (I
->GenSources(Setup
,SrcStats
) == false)
848 _error
->DumpErrors();
854 // close the Translation master files
855 for (vector
<PackageMap
>::reverse_iterator I
= PkgList
.rbegin(); I
!= PkgList
.rend(); ++I
)
856 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
857 delete I
->TransWriter
;
863 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
864 // ---------------------------------------------------------------------
865 static bool DoGenerateContents(Configuration
&Setup
,
866 vector
<PackageMap
> &PkgList
,
869 c1out
<< "Packages done, Starting contents." << endl
;
871 // Sort the contents file list by date
872 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
873 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
876 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
877 I
->CntCompress
,A
) == false)
878 time(&I
->ContentsMTime
);
880 I
->ContentsMTime
= A
.st_mtime
;
882 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
884 /* Now for Contents.. The process here is to do a make-like dependency
885 check. Each contents file is verified to be newer than the package files
886 that describe the debs it indexes. Since the package files contain
887 hashes of the .debs this means they have not changed either so the
888 contents must be up to date. */
889 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
890 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
892 // This record is not relevant
893 if (I
->ContentsDone
== true ||
894 I
->Contents
.empty() == true)
897 // Do not do everything if the user specified sections.
898 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
902 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
904 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
906 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
910 if (A
.st_mtime
> B
.st_mtime
)
914 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
915 MaxContentsChange
) == false)
916 _error
->DumpErrors();
919 if (MaxContentsChange
== 0)
921 c1out
<< "Hit contents update byte limit" << endl
;
930 // Generate - Full generate, using a config file /*{{{*/
931 // ---------------------------------------------------------------------
933 static bool Generate(CommandLine
&CmdL
)
935 struct CacheDB::Stats SrcStats
;
936 if (CmdL
.FileSize() < 2)
937 return ShowHelp(CmdL
);
939 struct timeval StartTime
;
940 gettimeofday(&StartTime
,0);
941 struct CacheDB::Stats Stats
;
943 // Read the configuration file.
945 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
948 vector
<PackageMap
> PkgList
;
949 LoadTree(PkgList
,Setup
);
950 LoadBinDir(PkgList
,Setup
);
952 // Sort by cache DB to improve IO locality.
953 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
954 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
957 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
959 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
962 c1out
<< "Skipping Packages/Sources generation" << endl
;
965 // do Contents if needed
966 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
967 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
970 struct timeval NewTime
;
971 gettimeofday(&NewTime
,0);
972 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
973 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
974 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
975 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
981 // Clean - Clean out the databases /*{{{*/
982 // ---------------------------------------------------------------------
984 static bool Clean(CommandLine
&CmdL
)
986 if (CmdL
.FileSize() != 2)
987 return ShowHelp(CmdL
);
989 // Read the configuration file.
991 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
994 vector
<PackageMap
> PkgList
;
995 LoadTree(PkgList
,Setup
);
996 LoadBinDir(PkgList
,Setup
);
998 // Sort by cache DB to improve IO locality.
999 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
1000 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
1002 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1004 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1006 if(I
->BinCacheDB
!= "")
1007 c0out
<< I
->BinCacheDB
<< endl
;
1008 if(I
->SrcCacheDB
!= "")
1009 c0out
<< I
->SrcCacheDB
<< endl
;
1010 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1011 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1012 if (DB
.Clean() == false)
1013 _error
->DumpErrors();
1014 if (DB_SRC
.Clean() == false)
1015 _error
->DumpErrors();
1017 string CacheDB
= I
->BinCacheDB
;
1018 string SrcCacheDB
= I
->SrcCacheDB
;
1019 while(I
!= PkgList
.end() &&
1020 I
->BinCacheDB
== CacheDB
&&
1021 I
->SrcCacheDB
== SrcCacheDB
)
1031 int main(int argc
, const char *argv
[])
1033 setlocale(LC_ALL
, "");
1034 CommandLine::Args Args
[] = {
1035 {'h',"help","help",0},
1036 {0,"md5","APT::FTPArchive::MD5",0},
1037 {0,"sha1","APT::FTPArchive::SHA1",0},
1038 {0,"sha256","APT::FTPArchive::SHA256",0},
1039 {'v',"version","version",0},
1040 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
1041 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
1042 {'q',"quiet","quiet",CommandLine::IntLevel
},
1043 {'q',"silent","quiet",CommandLine::IntLevel
},
1044 {0,"delink","APT::FTPArchive::DeLinkAct",0},
1045 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
1046 {0,"contents","APT::FTPArchive::Contents",0},
1047 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
1048 {'c',"config-file",0,CommandLine::ConfigFile
},
1049 {'o',"option",0,CommandLine::ArbItem
},
1051 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
1052 {"contents",&SimpleGenContents
},
1053 {"sources",&SimpleGenSources
},
1054 {"release",&SimpleGenRelease
},
1055 {"generate",&Generate
},
1060 // Parse the command line and initialize the package library
1061 CommandLine
CmdL(Args
,_config
);
1062 ParseCommandLine(CmdL
, Cmds
, Args
, &_config
, NULL
, argc
, argv
, ShowHelp
);
1064 _config
->CndSet("quiet",0);
1065 Quiet
= _config
->FindI("quiet",0);
1066 InitOutput(clog
.rdbuf());
1068 // Match the operation
1069 CmdL
.DispatchArg(Cmds
);
1071 if (_error
->empty() == false)
1073 bool Errors
= _error
->PendingError();
1074 _error
->DumpErrors();
1075 return Errors
== true?100:0;