]>
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>
36 #include "apt-ftparchive.h"
37 #include "multicompress.h"
47 ofstream
devnull("/dev/null");
50 // struct PackageMap - List of all package files in the config file /*{{{*/
51 // ---------------------------------------------------------------------
57 string InternalPrefix
;
62 // 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 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
111 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
112 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
113 bool GenContents(Configuration
&Setup
,
114 vector
<PackageMap
>::iterator Begin
,
115 vector
<PackageMap
>::iterator End
,
116 unsigned long &Left
);
118 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
119 ContentsDone(false), PkgDone(false), SrcDone(false),
124 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
125 // ---------------------------------------------------------------------
127 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
129 PathPrefix
= Block
.Find("PathPrefix");
131 if (Block
.FindB("External-Links",true) == false)
132 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
136 PkgCompress
= Block
.Find("Packages::Compress",
137 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
138 CntCompress
= Block
.Find("Contents::Compress",
139 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
140 SrcCompress
= Block
.Find("Sources::Compress",
141 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
143 SrcExt
= Block
.Find("Sources::Extensions",
144 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
145 PkgExt
= Block
.Find("Packages::Extensions",
146 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
148 Permissions
= Setup
.FindI("Default::FileMode",0644);
150 if (FLFile
.empty() == false)
151 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
157 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
158 // ---------------------------------------------------------------------
159 /* This generates the Package File described by this object. */
160 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
162 if (PkgFile
.empty() == true)
165 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
166 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
167 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
169 struct timeval StartTime
;
170 gettimeofday(&StartTime
,0);
174 // Create a package writer object.
175 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
176 flCombine(OverrideDir
,BinOverride
),
177 flCombine(OverrideDir
,ExtraOverride
),
179 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
180 return _error
->Error(_("Package extension list is too long"));
181 if (_error
->PendingError() == true)
182 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
184 Packages
.PathPrefix
= PathPrefix
;
185 Packages
.DirStrip
= ArchiveDir
;
186 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
188 Packages
.TransWriter
= TransWriter
;
189 Packages
.LongDescription
= LongDesc
;
191 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
192 Packages
.DeLinkLimit
= DeLinkLimit
;
194 // Create a compressor object
195 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
196 PkgCompress
,Permissions
);
197 Packages
.Output
= Comp
.Input
;
198 if (_error
->PendingError() == true)
199 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
201 c0out
<< ' ' << BaseDir
<< ":" << flush
;
203 // Do recursive directory searching
204 if (FLFile
.empty() == true)
206 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
211 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
215 Packages
.Output
= 0; // Just in case
217 // Finish compressing
218 unsigned long long Size
;
219 if (Comp
.Finalize(Size
) == false)
222 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
227 << SizeToStr(Size
) << "B ";
231 struct timeval NewTime
;
232 gettimeofday(&NewTime
,0);
233 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
234 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
236 c0out
<< Packages
.Stats
.Packages
<< " files " <<
237 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
238 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
239 TimeToStr((long)Delta
) << endl
;
241 Stats
.Add(Packages
.Stats
);
242 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
244 return !_error
->PendingError();
248 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
249 // ---------------------------------------------------------------------
250 /* This generates the Sources File described by this object. */
251 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
253 if (SrcFile
.empty() == true)
256 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
257 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
258 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
260 struct timeval StartTime
;
261 gettimeofday(&StartTime
,0);
265 // Create a package writer object.
266 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),
267 flCombine(OverrideDir
,BinOverride
),
268 flCombine(OverrideDir
,SrcOverride
),
269 flCombine(OverrideDir
,SrcExtraOverride
));
270 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
271 return _error
->Error(_("Source extension list is too long"));
272 if (_error
->PendingError() == true)
273 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
275 Sources
.PathPrefix
= PathPrefix
;
276 Sources
.DirStrip
= ArchiveDir
;
277 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
279 Sources
.DeLinkLimit
= DeLinkLimit
;
280 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
282 // Create a compressor object
283 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
284 SrcCompress
,Permissions
);
285 Sources
.Output
= Comp
.Input
;
286 if (_error
->PendingError() == true)
287 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
289 c0out
<< ' ' << BaseDir
<< ":" << flush
;
291 // Do recursive directory searching
292 if (FLFile
.empty() == true)
294 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
299 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
302 Sources
.Output
= 0; // Just in case
304 // Finish compressing
305 unsigned long long Size
;
306 if (Comp
.Finalize(Size
) == false)
309 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
314 << SizeToStr(Size
) << "B ";
318 struct timeval NewTime
;
319 gettimeofday(&NewTime
,0);
320 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
321 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
323 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
324 TimeToStr((long)Delta
) << endl
;
326 Stats
.Add(Sources
.Stats
);
327 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
329 return !_error
->PendingError();
332 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
333 // ---------------------------------------------------------------------
334 /* This generates the contents file partially described by this object.
335 It searches the given iterator range for other package files that map
336 into this contents file and includes their data as well when building. */
337 bool PackageMap::GenContents(Configuration
&Setup
,
338 vector
<PackageMap
>::iterator Begin
,
339 vector
<PackageMap
>::iterator End
,
342 if (Contents
.empty() == true)
348 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
349 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
350 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
352 struct timeval StartTime
;
353 gettimeofday(&StartTime
,0);
355 // Create a package writer object.
356 ContentsWriter
Contents("", Arch
);
357 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
358 return _error
->Error(_("Package extension list is too long"));
359 if (_error
->PendingError() == true)
362 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
363 CntCompress
,Permissions
);
364 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
365 Contents
.Output
= Comp
.Input
;
366 if (_error
->PendingError() == true)
369 // Write the header out.
370 if (ContentsHead
.empty() == false)
372 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
373 if (_error
->PendingError() == true)
376 unsigned long long Size
= Head
.Size();
377 unsigned char Buf
[4096];
380 unsigned long long ToRead
= Size
;
381 if (Size
> sizeof(Buf
))
382 ToRead
= sizeof(Buf
);
384 if (Head
.Read(Buf
,ToRead
) == false)
387 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
388 return _error
->Errno("fwrite",_("Error writing header to contents file"));
394 /* Go over all the package file records and parse all the package
395 files associated with this contents file into one great big honking
396 memory structure, then dump the sorted version */
397 c0out
<< ' ' << this->Contents
<< ":" << flush
;
398 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
400 if (I
->Contents
!= this->Contents
)
403 Contents
.Prefix
= ArchiveDir
;
404 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
405 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
408 I
->ContentsDone
= true;
413 // Finish compressing
414 unsigned long long Size
;
415 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
418 return _error
->Error(_("Error processing contents %s"),
419 this->Contents
.c_str());
424 c0out
<< " New " << SizeToStr(Size
) << "B ";
433 struct timeval NewTime
;
434 gettimeofday(&NewTime
,0);
435 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
436 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
438 c0out
<< Contents
.Stats
.Packages
<< " files " <<
439 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
440 TimeToStr((long)Delta
) << endl
;
446 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
447 // ---------------------------------------------------------------------
448 /* This populates the PkgList with all the possible permutations of the
449 section/arch lists. */
450 static void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
453 string DDir
= Setup
.Find("TreeDefault::Directory",
454 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
455 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
456 "$(DIST)/$(SECTION)/source/");
457 string DPkg
= Setup
.Find("TreeDefault::Packages",
458 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
459 string DTrans
= Setup
.Find("TreeDefault::Translation",
460 "$(DIST)/$(SECTION)/i18n/Translation-en");
461 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
462 "$(DIST)/$(SECTION)/");
463 string DContents
= Setup
.Find("TreeDefault::Contents",
464 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
465 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
466 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
467 "packages-$(ARCH).db");
468 string DSources
= Setup
.Find("TreeDefault::Sources",
469 "$(DIST)/$(SECTION)/source/Sources");
470 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
471 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
473 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
475 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
476 _config
->FindB("APT::FTPArchive::LongDescription", true));
477 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
479 // Process 'tree' type sections
480 const Configuration::Item
*Top
= Setup
.Tree("tree");
481 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
483 Configuration
Block(Top
);
484 string Dist
= Top
->Tag
;
486 // Parse the sections
487 string Tmp
= Block
.Find("Sections");
488 const char *Sections
= Tmp
.c_str();
490 while (ParseQuoteWord(Sections
,Section
) == true)
493 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
494 {"$(SECTION)",&Section
},
497 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
498 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
499 TranslationWriter
*TransWriter
;
500 if (DTrans
.empty() == false && LongDesc
== false)
502 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
503 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
504 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
505 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
510 string
const Tmp2
= Block
.Find("Architectures");
511 const char *Archs
= Tmp2
.c_str();
512 while (ParseQuoteWord(Archs
,Arch
) == true)
515 Itm
.Permissions
= Perms
;
516 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
517 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
519 if (stringcasecmp(Arch
,"source") == 0)
521 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
522 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
523 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
524 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
525 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
526 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
530 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
531 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
532 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
533 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
535 Itm
.LongDesc
= LongDesc
;
536 if (TransWriter
!= NULL
)
538 TransWriter
->IncreaseRefCounter();
539 Itm
.TransWriter
= TransWriter
;
541 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
542 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
543 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
544 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
547 Itm
.GetGeneral(Setup
,Block
);
548 PkgList
.push_back(Itm
);
550 // we didn't use this TransWriter, so we can release it
551 if (TransWriter
!= NULL
&& TransWriter
->GetRefCounter() == 0)
559 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
560 // ---------------------------------------------------------------------
562 static void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
564 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
566 // Process 'bindirectory' type sections
567 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
568 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
570 Configuration
Block(Top
);
573 Itm
.PkgFile
= Block
.Find("Packages");
574 Itm
.SrcFile
= Block
.Find("Sources");
575 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
576 Itm
.BinOverride
= Block
.Find("BinOverride");
577 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
578 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
579 Itm
.SrcOverride
= Block
.Find("SrcOverride");
580 Itm
.BaseDir
= Top
->Tag
;
581 Itm
.FLFile
= Block
.Find("FileList");
582 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
583 Itm
.Contents
= Block
.Find("Contents");
584 Itm
.ContentsHead
= Block
.Find("Contents::Header");
585 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
587 Itm
.GetGeneral(Setup
,Block
);
588 PkgList
.push_back(Itm
);
595 // ShowHelp - Show the help text /*{{{*/
596 // ---------------------------------------------------------------------
598 static bool ShowHelp(CommandLine
&)
600 ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,PACKAGE_VERSION
,
601 COMMON_ARCH
,__DATE__
,__TIME__
);
602 if (_config
->FindB("version") == true)
606 _("Usage: apt-ftparchive [options] command\n"
607 "Commands: packages binarypath [overridefile [pathprefix]]\n"
608 " sources srcpath [overridefile [pathprefix]]\n"
611 " generate config [groups]\n"
614 "apt-ftparchive generates index files for Debian archives. It supports\n"
615 "many styles of generation from fully automated to functional replacements\n"
616 "for dpkg-scanpackages and dpkg-scansources\n"
618 "apt-ftparchive generates Package files from a tree of .debs. The\n"
619 "Package file contains the contents of all the control fields from\n"
620 "each package as well as the MD5 hash and filesize. An override file\n"
621 "is supported to force the value of Priority and Section.\n"
623 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
624 "The --source-override option can be used to specify a src override file\n"
626 "The 'packages' and 'sources' command should be run in the root of the\n"
627 "tree. BinaryPath should point to the base of the recursive search and \n"
628 "override file should contain the override flags. Pathprefix is\n"
629 "appended to the filename fields if present. Example usage from the \n"
631 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
632 " dists/potato/main/binary-i386/Packages\n"
635 " -h This help text\n"
636 " --md5 Control MD5 generation\n"
637 " -s=? Source override file\n"
639 " -d=? Select the optional caching database\n"
640 " --no-delink Enable delinking debug mode\n"
641 " --contents Control contents file generation\n"
642 " -c=? Read this configuration file\n"
643 " -o=? Set an arbitrary configuration option") << endl
;
648 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
649 // ---------------------------------------------------------------------
650 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
651 static bool SimpleGenPackages(CommandLine
&CmdL
)
653 if (CmdL
.FileSize() < 2)
654 return ShowHelp(CmdL
);
657 if (CmdL
.FileSize() >= 3)
658 Override
= CmdL
.FileList
[2];
660 // Create a package writer object.
661 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
662 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
663 if (_error
->PendingError() == true)
666 if (CmdL
.FileSize() >= 4)
667 Packages
.PathPrefix
= CmdL
.FileList
[3];
669 // Do recursive directory searching
670 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
676 // SimpleGenContents - Generate a Contents listing /*{{{*/
677 // ---------------------------------------------------------------------
679 static bool SimpleGenContents(CommandLine
&CmdL
)
681 if (CmdL
.FileSize() < 2)
682 return ShowHelp(CmdL
);
684 // Create a package writer object.
685 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
686 if (_error
->PendingError() == true)
689 // Do recursive directory searching
690 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
698 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
699 // ---------------------------------------------------------------------
700 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
701 static bool SimpleGenSources(CommandLine
&CmdL
)
703 if (CmdL
.FileSize() < 2)
704 return ShowHelp(CmdL
);
707 if (CmdL
.FileSize() >= 3)
708 Override
= CmdL
.FileList
[2];
711 if (Override
.empty() == false)
712 SOverride
= Override
+ ".src";
714 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
717 // Create a package writer object.
718 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
719 if (_error
->PendingError() == true)
722 if (CmdL
.FileSize() >= 4)
723 Sources
.PathPrefix
= CmdL
.FileList
[3];
725 // Do recursive directory searching
726 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
732 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
733 // ---------------------------------------------------------------------
734 static bool SimpleGenRelease(CommandLine
&CmdL
)
736 if (CmdL
.FileSize() < 2)
737 return ShowHelp(CmdL
);
739 string Dir
= CmdL
.FileList
[1];
741 ReleaseWriter
Release("");
742 Release
.DirStrip
= Dir
;
744 if (_error
->PendingError() == true)
747 if (Release
.RecursiveScan(Dir
) == false)
756 // Generate - Full generate, using a config file /*{{{*/
757 // ---------------------------------------------------------------------
759 static bool Generate(CommandLine
&CmdL
)
761 struct CacheDB::Stats SrcStats
;
762 if (CmdL
.FileSize() < 2)
763 return ShowHelp(CmdL
);
765 struct timeval StartTime
;
766 gettimeofday(&StartTime
,0);
767 struct CacheDB::Stats Stats
;
769 // Read the configuration file.
771 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
774 vector
<PackageMap
> PkgList
;
775 LoadTree(PkgList
,Setup
);
776 LoadBinDir(PkgList
,Setup
);
778 // Sort by cache DB to improve IO locality.
779 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
782 if (CmdL
.FileSize() <= 2)
784 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
785 if (I
->GenPackages(Setup
,Stats
) == false)
786 _error
->DumpErrors();
787 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
788 if (I
->GenSources(Setup
,SrcStats
) == false)
789 _error
->DumpErrors();
793 // Make a choice list out of the package list..
794 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
795 RxChoiceList
*End
= List
;
796 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
798 End
->UserData
= &(*I
);
799 End
->Str
= I
->BaseDir
.c_str();
802 End
->UserData
= &(*I
);
803 End
->Str
= I
->Tag
.c_str();
809 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
812 return _error
->Error(_("No selections matched"));
814 _error
->DumpErrors();
816 // Do the generation for Packages
817 for (End
= List
; End
->Str
!= 0; End
++)
819 if (End
->Hit
== false)
822 PackageMap
*I
= (PackageMap
*)End
->UserData
;
823 if (I
->PkgDone
== true)
825 if (I
->GenPackages(Setup
,Stats
) == false)
826 _error
->DumpErrors();
829 // Do the generation for Sources
830 for (End
= List
; End
->Str
!= 0; End
++)
832 if (End
->Hit
== false)
835 PackageMap
*I
= (PackageMap
*)End
->UserData
;
836 if (I
->SrcDone
== true)
838 if (I
->GenSources(Setup
,SrcStats
) == false)
839 _error
->DumpErrors();
845 // close the Translation master files
846 for (vector
<PackageMap
>::reverse_iterator I
= PkgList
.rbegin(); I
!= PkgList
.rend(); ++I
)
847 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
848 delete I
->TransWriter
;
850 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
853 c1out
<< "Packages done, Starting contents." << endl
;
855 // Sort the contents file list by date
856 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
857 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
860 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
861 I
->CntCompress
,A
) == false)
862 time(&I
->ContentsMTime
);
864 I
->ContentsMTime
= A
.st_mtime
;
866 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
868 /* Now for Contents.. The process here is to do a make-like dependency
869 check. Each contents file is verified to be newer than the package files
870 that describe the debs it indexes. Since the package files contain
871 hashes of the .debs this means they have not changed either so the
872 contents must be up to date. */
873 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
874 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
876 // This record is not relevant
877 if (I
->ContentsDone
== true ||
878 I
->Contents
.empty() == true)
881 // Do not do everything if the user specified sections.
882 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
886 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
888 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
890 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
894 if (A
.st_mtime
> B
.st_mtime
)
898 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
899 MaxContentsChange
) == false)
900 _error
->DumpErrors();
903 if (MaxContentsChange
== 0)
905 c1out
<< "Hit contents update byte limit" << endl
;
910 struct timeval NewTime
;
911 gettimeofday(&NewTime
,0);
912 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
913 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
914 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
915 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
920 // Clean - Clean out the databases /*{{{*/
921 // ---------------------------------------------------------------------
923 static bool Clean(CommandLine
&CmdL
)
925 if (CmdL
.FileSize() != 2)
926 return ShowHelp(CmdL
);
928 // Read the configuration file.
930 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
933 vector
<PackageMap
> PkgList
;
934 LoadTree(PkgList
,Setup
);
935 LoadBinDir(PkgList
,Setup
);
937 // Sort by cache DB to improve IO locality.
938 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
940 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
942 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
944 c0out
<< I
->BinCacheDB
<< endl
;
945 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
946 if (DB
.Clean() == false)
947 _error
->DumpErrors();
949 string CacheDB
= I
->BinCacheDB
;
950 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; ++I
);
957 int main(int argc
, const char *argv
[])
959 setlocale(LC_ALL
, "");
960 CommandLine::Args Args
[] = {
961 {'h',"help","help",0},
962 {0,"md5","APT::FTPArchive::MD5",0},
963 {0,"sha1","APT::FTPArchive::SHA1",0},
964 {0,"sha256","APT::FTPArchive::SHA256",0},
965 {'v',"version","version",0},
966 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
967 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
968 {'q',"quiet","quiet",CommandLine::IntLevel
},
969 {'q',"silent","quiet",CommandLine::IntLevel
},
970 {0,"delink","APT::FTPArchive::DeLinkAct",0},
971 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
972 {0,"contents","APT::FTPArchive::Contents",0},
973 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
974 {'c',"config-file",0,CommandLine::ConfigFile
},
975 {'o',"option",0,CommandLine::ArbItem
},
977 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
978 {"contents",&SimpleGenContents
},
979 {"sources",&SimpleGenSources
},
980 {"release",&SimpleGenRelease
},
981 {"generate",&Generate
},
986 // Parse the command line and initialize the package library
987 CommandLine
CmdL(Args
,_config
);
988 if (pkgInitConfig(*_config
) == false || CmdL
.Parse(argc
,argv
) == false)
990 _error
->DumpErrors();
994 // See if the help should be shown
995 if (_config
->FindB("help") == true ||
996 _config
->FindB("version") == true ||
997 CmdL
.FileSize() == 0)
1003 // Setup the output streams
1004 c0out
.rdbuf(clog
.rdbuf());
1005 c1out
.rdbuf(clog
.rdbuf());
1006 c2out
.rdbuf(clog
.rdbuf());
1007 Quiet
= _config
->FindI("quiet",0);
1009 c0out
.rdbuf(devnull
.rdbuf());
1011 c1out
.rdbuf(devnull
.rdbuf());
1013 // Match the operation
1014 CmdL
.DispatchArg(Cmds
);
1016 if (_error
->empty() == false)
1018 bool Errors
= _error
->PendingError();
1019 _error
->DumpErrors();
1020 return Errors
== true?100:0;