]>
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>
38 #include "apt-ftparchive.h"
39 #include "multicompress.h"
49 ofstream
devnull("/dev/null");
52 // struct PackageMap - List of all package files in the config file /*{{{*/
53 // ---------------------------------------------------------------------
59 string InternalPrefix
;
64 // Stuff for the Package File
71 // We generate for this given arch
74 // Stuff for the Source File
77 string SrcExtraOverride
;
79 // Translation master file
81 TranslationWriter
*TransWriter
;
93 unsigned int DeLinkLimit
;
101 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
103 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
104 {return x
.ContentsMTime
< y
.ContentsMTime
;};
107 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
109 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
110 {return x
.BinCacheDB
< y
.BinCacheDB
;};
113 struct SrcDBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
115 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
116 {return x
.SrcCacheDB
< y
.SrcCacheDB
;};
119 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
120 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
121 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
122 bool GenContents(Configuration
&Setup
,
123 vector
<PackageMap
>::iterator Begin
,
124 vector
<PackageMap
>::iterator End
,
125 unsigned long &Left
);
127 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
128 ContentsDone(false), PkgDone(false), SrcDone(false),
133 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
134 // ---------------------------------------------------------------------
136 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
138 PathPrefix
= Block
.Find("PathPrefix");
140 if (Block
.FindB("External-Links",true) == false)
141 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
145 PkgCompress
= Block
.Find("Packages::Compress",
146 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
147 CntCompress
= Block
.Find("Contents::Compress",
148 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
149 SrcCompress
= Block
.Find("Sources::Compress",
150 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
152 SrcExt
= Block
.Find("Sources::Extensions",
153 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
154 PkgExt
= Block
.Find("Packages::Extensions",
155 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
157 Permissions
= Setup
.FindI("Default::FileMode",0644);
159 if (FLFile
.empty() == false)
160 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
166 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
167 // ---------------------------------------------------------------------
168 /* This generates the Package File described by this object. */
169 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
171 if (PkgFile
.empty() == true)
174 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
175 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
176 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
178 struct timeval StartTime
;
179 gettimeofday(&StartTime
,0);
183 // Create a package writer object.
184 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
185 flCombine(OverrideDir
,BinOverride
),
186 flCombine(OverrideDir
,ExtraOverride
),
188 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
189 return _error
->Error(_("Package extension list is too long"));
190 if (_error
->PendingError() == true)
191 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
193 Packages
.PathPrefix
= PathPrefix
;
194 Packages
.DirStrip
= ArchiveDir
;
195 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
197 Packages
.TransWriter
= TransWriter
;
198 Packages
.LongDescription
= LongDesc
;
200 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
201 Packages
.DeLinkLimit
= DeLinkLimit
;
203 // Create a compressor object
204 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
205 PkgCompress
,Permissions
);
206 Packages
.Output
= Comp
.Input
;
207 if (_error
->PendingError() == true)
208 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
210 c0out
<< ' ' << BaseDir
<< ":" << flush
;
212 // Do recursive directory searching
213 if (FLFile
.empty() == true)
215 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
220 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
224 Packages
.Output
= 0; // Just in case
226 // Finish compressing
227 unsigned long long Size
;
228 if (Comp
.Finalize(Size
) == false)
231 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
236 << SizeToStr(Size
) << "B ";
240 struct timeval NewTime
;
241 gettimeofday(&NewTime
,0);
242 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
243 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
245 c0out
<< Packages
.Stats
.Packages
<< " files " <<
246 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
247 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
248 TimeToStr((long)Delta
) << endl
;
250 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
251 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
253 Stats
.Add(Packages
.Stats
);
254 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
256 return !_error
->PendingError();
260 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
261 // ---------------------------------------------------------------------
262 /* This generates the Sources File described by this object. */
263 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
265 if (SrcFile
.empty() == true)
268 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
269 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
270 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
272 struct timeval StartTime
;
273 gettimeofday(&StartTime
,0);
277 // Create a package writer object.
278 SourcesWriter
Sources(flCombine(CacheDir
, SrcCacheDB
),
279 flCombine(OverrideDir
,BinOverride
),
280 flCombine(OverrideDir
,SrcOverride
),
281 flCombine(OverrideDir
,SrcExtraOverride
));
282 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
283 return _error
->Error(_("Source extension list is too long"));
284 if (_error
->PendingError() == true)
285 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
287 Sources
.PathPrefix
= PathPrefix
;
288 Sources
.DirStrip
= ArchiveDir
;
289 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
291 Sources
.DeLinkLimit
= DeLinkLimit
;
292 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
294 // Create a compressor object
295 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
296 SrcCompress
,Permissions
);
297 Sources
.Output
= Comp
.Input
;
298 if (_error
->PendingError() == true)
299 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
301 c0out
<< ' ' << BaseDir
<< ":" << flush
;
303 // Do recursive directory searching
304 if (FLFile
.empty() == true)
306 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
311 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
314 Sources
.Output
= 0; // Just in case
316 // Finish compressing
317 unsigned long long Size
;
318 if (Comp
.Finalize(Size
) == false)
321 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
326 << SizeToStr(Size
) << "B ";
330 struct timeval NewTime
;
331 gettimeofday(&NewTime
,0);
332 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
333 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
335 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
336 TimeToStr((long)Delta
) << endl
;
338 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
339 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
341 Stats
.Add(Sources
.Stats
);
342 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
344 return !_error
->PendingError();
347 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
348 // ---------------------------------------------------------------------
349 /* This generates the contents file partially described by this object.
350 It searches the given iterator range for other package files that map
351 into this contents file and includes their data as well when building. */
352 bool PackageMap::GenContents(Configuration
&Setup
,
353 vector
<PackageMap
>::iterator Begin
,
354 vector
<PackageMap
>::iterator End
,
357 if (Contents
.empty() == true)
363 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
364 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
365 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
367 struct timeval StartTime
;
368 gettimeofday(&StartTime
,0);
370 // Create a package writer object.
371 ContentsWriter
Contents("", Arch
);
372 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
373 return _error
->Error(_("Package extension list is too long"));
374 if (_error
->PendingError() == true)
377 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
378 CntCompress
,Permissions
);
379 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
380 Contents
.Output
= Comp
.Input
;
381 if (_error
->PendingError() == true)
384 // Write the header out.
385 if (ContentsHead
.empty() == false)
387 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
388 if (_error
->PendingError() == true)
391 unsigned long long Size
= Head
.Size();
392 unsigned char Buf
[4096];
395 unsigned long long ToRead
= Size
;
396 if (Size
> sizeof(Buf
))
397 ToRead
= sizeof(Buf
);
399 if (Head
.Read(Buf
,ToRead
) == false)
402 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
403 return _error
->Errno("fwrite",_("Error writing header to contents file"));
409 /* Go over all the package file records and parse all the package
410 files associated with this contents file into one great big honking
411 memory structure, then dump the sorted version */
412 c0out
<< ' ' << this->Contents
<< ":" << flush
;
413 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
415 if (I
->Contents
!= this->Contents
)
418 Contents
.Prefix
= ArchiveDir
;
419 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
420 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
423 I
->ContentsDone
= true;
428 // Finish compressing
429 unsigned long long Size
;
430 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
433 return _error
->Error(_("Error processing contents %s"),
434 this->Contents
.c_str());
439 c0out
<< " New " << SizeToStr(Size
) << "B ";
448 struct timeval NewTime
;
449 gettimeofday(&NewTime
,0);
450 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
451 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
453 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
454 c0out
<< " Misses in Cache: " << Contents
.Stats
.Misses
<< endl
;
456 c0out
<< Contents
.Stats
.Packages
<< " files " <<
457 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
458 TimeToStr((long)Delta
) << endl
;
464 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
465 // ---------------------------------------------------------------------
466 /* This populates the PkgList with all the possible permutations of the
467 section/arch lists. */
468 static void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
471 string DDir
= Setup
.Find("TreeDefault::Directory",
472 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
473 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
474 "$(DIST)/$(SECTION)/source/");
475 string DPkg
= Setup
.Find("TreeDefault::Packages",
476 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
477 string DTrans
= Setup
.Find("TreeDefault::Translation",
478 "$(DIST)/$(SECTION)/i18n/Translation-en");
479 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
480 "$(DIST)/$(SECTION)/");
481 string DContents
= Setup
.Find("TreeDefault::Contents",
482 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
483 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
484 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
485 "packages-$(ARCH).db");
486 string SrcDBCache
= Setup
.Find("TreeDefault::SrcCacheDB",
487 "sources-$(SECTION).db");
488 string DSources
= Setup
.Find("TreeDefault::Sources",
489 "$(DIST)/$(SECTION)/source/Sources");
490 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
491 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
493 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
495 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
496 _config
->FindB("APT::FTPArchive::LongDescription", true));
497 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
499 // Process 'tree' type sections
500 const Configuration::Item
*Top
= Setup
.Tree("tree");
501 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
503 Configuration
Block(Top
);
504 string Dist
= Top
->Tag
;
506 // Parse the sections
507 string Tmp
= Block
.Find("Sections");
508 const char *Sections
= Tmp
.c_str();
510 while (ParseQuoteWord(Sections
,Section
) == true)
513 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
514 {"$(SECTION)",&Section
},
517 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
518 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
519 TranslationWriter
*TransWriter
;
520 if (DTrans
.empty() == false && LongDesc
== false)
522 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
523 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
524 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
525 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
530 string
const Tmp2
= Block
.Find("Architectures");
531 const char *Archs
= Tmp2
.c_str();
532 while (ParseQuoteWord(Archs
,Arch
) == true)
535 Itm
.Permissions
= Perms
;
536 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
537 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
539 if (stringcasecmp(Arch
,"source") == 0)
541 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
542 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
543 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
544 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
545 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
546 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
547 Itm
.SrcCacheDB
= SubstVar(Block
.Find("SrcCacheDB",SrcDBCache
.c_str()),Vars
);
551 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
552 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
553 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
554 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
556 Itm
.LongDesc
= LongDesc
;
557 if (TransWriter
!= NULL
)
559 TransWriter
->IncreaseRefCounter();
560 Itm
.TransWriter
= TransWriter
;
562 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
563 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
564 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
565 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
568 Itm
.GetGeneral(Setup
,Block
);
569 PkgList
.push_back(Itm
);
571 // we didn't use this TransWriter, so we can release it
572 if (TransWriter
!= NULL
&& TransWriter
->GetRefCounter() == 0)
580 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
581 // ---------------------------------------------------------------------
583 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
585 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
587 // Process 'bindirectory' type sections
588 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
589 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
591 Configuration
Block(Top
);
594 Itm
.PkgFile
= Block
.Find("Packages");
595 Itm
.SrcFile
= Block
.Find("Sources");
596 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
597 Itm
.SrcCacheDB
= Block
.Find("SrcCacheDB");
598 Itm
.BinOverride
= Block
.Find("BinOverride");
599 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
600 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
601 Itm
.SrcOverride
= Block
.Find("SrcOverride");
602 Itm
.BaseDir
= Top
->Tag
;
603 Itm
.FLFile
= Block
.Find("FileList");
604 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
605 Itm
.Contents
= Block
.Find("Contents");
606 Itm
.ContentsHead
= Block
.Find("Contents::Header");
607 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
609 Itm
.GetGeneral(Setup
,Block
);
610 PkgList
.push_back(Itm
);
617 // ShowHelp - Show the help text /*{{{*/
618 // ---------------------------------------------------------------------
620 static bool ShowHelp(CommandLine
&)
622 ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,PACKAGE_VERSION
,
623 COMMON_ARCH
,__DATE__
,__TIME__
);
624 if (_config
->FindB("version") == true)
628 _("Usage: apt-ftparchive [options] command\n"
629 "Commands: packages binarypath [overridefile [pathprefix]]\n"
630 " sources srcpath [overridefile [pathprefix]]\n"
633 " generate config [groups]\n"
636 "apt-ftparchive generates index files for Debian archives. It supports\n"
637 "many styles of generation from fully automated to functional replacements\n"
638 "for dpkg-scanpackages and dpkg-scansources\n"
640 "apt-ftparchive generates Package files from a tree of .debs. The\n"
641 "Package file contains the contents of all the control fields from\n"
642 "each package as well as the MD5 hash and filesize. An override file\n"
643 "is supported to force the value of Priority and Section.\n"
645 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
646 "The --source-override option can be used to specify a src override file\n"
648 "The 'packages' and 'sources' command should be run in the root of the\n"
649 "tree. BinaryPath should point to the base of the recursive search and \n"
650 "override file should contain the override flags. Pathprefix is\n"
651 "appended to the filename fields if present. Example usage from the \n"
653 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
654 " dists/potato/main/binary-i386/Packages\n"
657 " -h This help text\n"
658 " --md5 Control MD5 generation\n"
659 " -s=? Source override file\n"
661 " -d=? Select the optional caching database\n"
662 " --no-delink Enable delinking debug mode\n"
663 " --contents Control contents file generation\n"
664 " -c=? Read this configuration file\n"
665 " -o=? Set an arbitrary configuration option") << endl
;
670 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
671 // ---------------------------------------------------------------------
672 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
673 static bool SimpleGenPackages(CommandLine
&CmdL
)
675 if (CmdL
.FileSize() < 2)
676 return ShowHelp(CmdL
);
679 if (CmdL
.FileSize() >= 3)
680 Override
= CmdL
.FileList
[2];
682 // Create a package writer object.
683 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
684 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
685 if (_error
->PendingError() == true)
688 if (CmdL
.FileSize() >= 4)
689 Packages
.PathPrefix
= CmdL
.FileList
[3];
691 // Do recursive directory searching
692 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
695 // Give some stats if asked for
696 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
697 c0out
<< " Misses in Cache: " << Packages
.Stats
.Misses
<< endl
;
702 // SimpleGenContents - Generate a Contents listing /*{{{*/
703 // ---------------------------------------------------------------------
705 static bool SimpleGenContents(CommandLine
&CmdL
)
707 if (CmdL
.FileSize() < 2)
708 return ShowHelp(CmdL
);
710 // Create a package writer object.
711 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
712 if (_error
->PendingError() == true)
715 // Do recursive directory searching
716 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
724 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
725 // ---------------------------------------------------------------------
726 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
727 static bool SimpleGenSources(CommandLine
&CmdL
)
729 if (CmdL
.FileSize() < 2)
730 return ShowHelp(CmdL
);
733 if (CmdL
.FileSize() >= 3)
734 Override
= CmdL
.FileList
[2];
737 if (Override
.empty() == false)
738 SOverride
= Override
+ ".src";
740 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
743 // Create a package writer object.
744 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
745 if (_error
->PendingError() == true)
748 if (CmdL
.FileSize() >= 4)
749 Sources
.PathPrefix
= CmdL
.FileList
[3];
751 // Do recursive directory searching
752 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
755 // Give some stats if asked for
756 if(_config
->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
757 c0out
<< " Misses in Cache: " << Sources
.Stats
.Misses
<< endl
;
762 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
763 // ---------------------------------------------------------------------
764 static bool SimpleGenRelease(CommandLine
&CmdL
)
766 if (CmdL
.FileSize() < 2)
767 return ShowHelp(CmdL
);
769 string Dir
= CmdL
.FileList
[1];
771 ReleaseWriter
Release("");
772 Release
.DirStrip
= Dir
;
774 if (_error
->PendingError() == true)
777 if (Release
.RecursiveScan(Dir
) == false)
786 // DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
787 // ---------------------------------------------------------------------
788 static bool DoGeneratePackagesAndSources(Configuration
&Setup
,
789 vector
<PackageMap
> &PkgList
,
790 struct CacheDB::Stats
&SrcStats
,
791 struct CacheDB::Stats
&Stats
,
794 if (CmdL
.FileSize() <= 2)
796 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
797 if (I
->GenPackages(Setup
,Stats
) == false)
798 _error
->DumpErrors();
799 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
800 if (I
->GenSources(Setup
,SrcStats
) == false)
801 _error
->DumpErrors();
805 // Make a choice list out of the package list..
806 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
807 RxChoiceList
*End
= List
;
808 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
810 End
->UserData
= &(*I
);
811 End
->Str
= I
->BaseDir
.c_str();
814 End
->UserData
= &(*I
);
815 End
->Str
= I
->Tag
.c_str();
821 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
824 return _error
->Error(_("No selections matched"));
826 _error
->DumpErrors();
828 // Do the generation for Packages
829 for (End
= List
; End
->Str
!= 0; End
++)
831 if (End
->Hit
== false)
834 PackageMap
*I
= (PackageMap
*)End
->UserData
;
835 if (I
->PkgDone
== true)
837 if (I
->GenPackages(Setup
,Stats
) == false)
838 _error
->DumpErrors();
841 // Do the generation for Sources
842 for (End
= List
; End
->Str
!= 0; End
++)
844 if (End
->Hit
== false)
847 PackageMap
*I
= (PackageMap
*)End
->UserData
;
848 if (I
->SrcDone
== true)
850 if (I
->GenSources(Setup
,SrcStats
) == false)
851 _error
->DumpErrors();
857 // close the Translation master files
858 for (vector
<PackageMap
>::reverse_iterator I
= PkgList
.rbegin(); I
!= PkgList
.rend(); ++I
)
859 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
860 delete I
->TransWriter
;
866 // DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
867 // ---------------------------------------------------------------------
868 static bool DoGenerateContents(Configuration
&Setup
,
869 vector
<PackageMap
> &PkgList
,
872 c1out
<< "Packages done, Starting contents." << endl
;
874 // Sort the contents file list by date
875 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
876 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
879 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
880 I
->CntCompress
,A
) == false)
881 time(&I
->ContentsMTime
);
883 I
->ContentsMTime
= A
.st_mtime
;
885 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
887 /* Now for Contents.. The process here is to do a make-like dependency
888 check. Each contents file is verified to be newer than the package files
889 that describe the debs it indexes. Since the package files contain
890 hashes of the .debs this means they have not changed either so the
891 contents must be up to date. */
892 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
893 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
895 // This record is not relevant
896 if (I
->ContentsDone
== true ||
897 I
->Contents
.empty() == true)
900 // Do not do everything if the user specified sections.
901 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
905 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
907 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
909 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
913 if (A
.st_mtime
> B
.st_mtime
)
917 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
918 MaxContentsChange
) == false)
919 _error
->DumpErrors();
922 if (MaxContentsChange
== 0)
924 c1out
<< "Hit contents update byte limit" << endl
;
933 // Generate - Full generate, using a config file /*{{{*/
934 // ---------------------------------------------------------------------
936 static bool Generate(CommandLine
&CmdL
)
938 struct CacheDB::Stats SrcStats
;
939 if (CmdL
.FileSize() < 2)
940 return ShowHelp(CmdL
);
942 struct timeval StartTime
;
943 gettimeofday(&StartTime
,0);
944 struct CacheDB::Stats Stats
;
946 // Read the configuration file.
948 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
951 vector
<PackageMap
> PkgList
;
952 LoadTree(PkgList
,Setup
);
953 LoadBinDir(PkgList
,Setup
);
955 // Sort by cache DB to improve IO locality.
956 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
957 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
960 if (_config
->FindB("APT::FTPArchive::ContentsOnly", false) == false)
962 if(DoGeneratePackagesAndSources(Setup
, PkgList
, SrcStats
, Stats
, CmdL
) == false)
965 c1out
<< "Skipping Packages/Sources generation" << endl
;
968 // do Contents if needed
969 if (_config
->FindB("APT::FTPArchive::Contents", true) == true)
970 if (DoGenerateContents(Setup
, PkgList
, CmdL
) == false)
973 struct timeval NewTime
;
974 gettimeofday(&NewTime
,0);
975 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
976 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
977 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
978 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
984 // Clean - Clean out the databases /*{{{*/
985 // ---------------------------------------------------------------------
987 static bool Clean(CommandLine
&CmdL
)
989 if (CmdL
.FileSize() != 2)
990 return ShowHelp(CmdL
);
992 // Read the configuration file.
994 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
997 vector
<PackageMap
> PkgList
;
998 LoadTree(PkgList
,Setup
);
999 LoadBinDir(PkgList
,Setup
);
1001 // Sort by cache DB to improve IO locality.
1002 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
1003 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::SrcDBCompare());
1005 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
1007 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
1009 if(I
->BinCacheDB
!= "")
1010 c0out
<< I
->BinCacheDB
<< endl
;
1011 if(I
->SrcCacheDB
!= "")
1012 c0out
<< I
->SrcCacheDB
<< endl
;
1013 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
1014 CacheDB
DB_SRC(flCombine(CacheDir
,I
->SrcCacheDB
));
1015 if (DB
.Clean() == false)
1016 _error
->DumpErrors();
1017 if (DB_SRC
.Clean() == false)
1018 _error
->DumpErrors();
1020 string CacheDB
= I
->BinCacheDB
;
1021 string SrcCacheDB
= I
->SrcCacheDB
;
1022 while(I
!= PkgList
.end() &&
1023 I
->BinCacheDB
== CacheDB
&&
1024 I
->SrcCacheDB
== SrcCacheDB
)
1034 int main(int argc
, const char *argv
[])
1036 setlocale(LC_ALL
, "");
1037 CommandLine::Args Args
[] = {
1038 {'h',"help","help",0},
1039 {0,"md5","APT::FTPArchive::MD5",0},
1040 {0,"sha1","APT::FTPArchive::SHA1",0},
1041 {0,"sha256","APT::FTPArchive::SHA256",0},
1042 {'v',"version","version",0},
1043 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
1044 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
1045 {'q',"quiet","quiet",CommandLine::IntLevel
},
1046 {'q',"silent","quiet",CommandLine::IntLevel
},
1047 {0,"delink","APT::FTPArchive::DeLinkAct",0},
1048 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
1049 {0,"contents","APT::FTPArchive::Contents",0},
1050 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
1051 {'c',"config-file",0,CommandLine::ConfigFile
},
1052 {'o',"option",0,CommandLine::ArbItem
},
1054 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
1055 {"contents",&SimpleGenContents
},
1056 {"sources",&SimpleGenSources
},
1057 {"release",&SimpleGenRelease
},
1058 {"generate",&Generate
},
1063 // Parse the command line and initialize the package library
1064 CommandLine
CmdL(Args
,_config
);
1065 ParseCommandLine(CmdL
, Cmds
, Args
, &_config
, NULL
, argc
, argv
, ShowHelp
);
1067 // Setup the output streams
1068 c0out
.rdbuf(clog
.rdbuf());
1069 c1out
.rdbuf(clog
.rdbuf());
1070 c2out
.rdbuf(clog
.rdbuf());
1071 Quiet
= _config
->FindI("quiet",0);
1073 c0out
.rdbuf(devnull
.rdbuf());
1075 c1out
.rdbuf(devnull
.rdbuf());
1077 // Match the operation
1078 CmdL
.DispatchArg(Cmds
);
1080 if (_error
->empty() == false)
1082 bool Errors
= _error
->PendingError();
1083 _error
->DumpErrors();
1084 return Errors
== true?100:0;