]>
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>
26 #include "apt-ftparchive.h"
28 #include "multicompress.h"
38 ofstream
devnull("/dev/null");
41 // struct PackageMap - List of all package files in the config file /*{{{*/
42 // ---------------------------------------------------------------------
48 string InternalPrefix
;
53 // Stuff for the Package File
59 // We generate for this given arch
62 // Stuff for the Source File
65 string SrcExtraOverride
;
67 // Translation master file
69 TranslationWriter
*TransWriter
;
81 unsigned int DeLinkLimit
;
89 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
91 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
92 {return x
.ContentsMTime
< y
.ContentsMTime
;};
95 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
97 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
98 {return x
.BinCacheDB
< y
.BinCacheDB
;};
101 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
102 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
103 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
104 bool GenContents(Configuration
&Setup
,
105 vector
<PackageMap
>::iterator Begin
,
106 vector
<PackageMap
>::iterator End
,
107 unsigned long &Left
);
109 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
110 ContentsDone(false), PkgDone(false), SrcDone(false),
115 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
116 // ---------------------------------------------------------------------
118 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
120 PathPrefix
= Block
.Find("PathPrefix");
122 if (Block
.FindB("External-Links",true) == false)
123 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
127 PkgCompress
= Block
.Find("Packages::Compress",
128 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
129 CntCompress
= Block
.Find("Contents::Compress",
130 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
131 SrcCompress
= Block
.Find("Sources::Compress",
132 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
134 SrcExt
= Block
.Find("Sources::Extensions",
135 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
136 PkgExt
= Block
.Find("Packages::Extensions",
137 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
139 Permissions
= Setup
.FindI("Default::FileMode",0644);
141 if (FLFile
.empty() == false)
142 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
148 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
149 // ---------------------------------------------------------------------
150 /* This generates the Package File described by this object. */
151 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
153 if (PkgFile
.empty() == true)
156 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
157 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
158 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
160 struct timeval StartTime
;
161 gettimeofday(&StartTime
,0);
165 // Create a package writer object.
166 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
167 flCombine(OverrideDir
,BinOverride
),
168 flCombine(OverrideDir
,ExtraOverride
),
170 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
171 return _error
->Error(_("Package extension list is too long"));
172 if (_error
->PendingError() == true)
173 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
175 Packages
.PathPrefix
= PathPrefix
;
176 Packages
.DirStrip
= ArchiveDir
;
177 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
179 Packages
.TransWriter
= TransWriter
;
180 Packages
.LongDescription
= LongDesc
;
182 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
183 Packages
.DeLinkLimit
= DeLinkLimit
;
185 // Create a compressor object
186 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
187 PkgCompress
,Permissions
);
188 Packages
.Output
= Comp
.Input
;
189 if (_error
->PendingError() == true)
190 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
192 c0out
<< ' ' << BaseDir
<< ":" << flush
;
194 // Do recursive directory searching
195 if (FLFile
.empty() == true)
197 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
202 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
206 Packages
.Output
= 0; // Just in case
208 // Finish compressing
209 unsigned long long Size
;
210 if (Comp
.Finalize(Size
) == false)
213 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
218 << SizeToStr(Size
) << "B ";
222 struct timeval NewTime
;
223 gettimeofday(&NewTime
,0);
224 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
225 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
227 c0out
<< Packages
.Stats
.Packages
<< " files " <<
228 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
229 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
230 TimeToStr((long)Delta
) << endl
;
232 Stats
.Add(Packages
.Stats
);
233 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
235 return !_error
->PendingError();
239 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
240 // ---------------------------------------------------------------------
241 /* This generates the Sources File described by this object. */
242 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
244 if (SrcFile
.empty() == true)
247 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
248 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
249 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
251 struct timeval StartTime
;
252 gettimeofday(&StartTime
,0);
256 // Create a package writer object.
257 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),
258 flCombine(OverrideDir
,BinOverride
),
259 flCombine(OverrideDir
,SrcOverride
),
260 flCombine(OverrideDir
,SrcExtraOverride
));
261 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
262 return _error
->Error(_("Source extension list is too long"));
263 if (_error
->PendingError() == true)
264 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
266 Sources
.PathPrefix
= PathPrefix
;
267 Sources
.DirStrip
= ArchiveDir
;
268 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
270 Sources
.DeLinkLimit
= DeLinkLimit
;
271 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
273 // Create a compressor object
274 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
275 SrcCompress
,Permissions
);
276 Sources
.Output
= Comp
.Input
;
277 if (_error
->PendingError() == true)
278 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
280 c0out
<< ' ' << BaseDir
<< ":" << flush
;
282 // Do recursive directory searching
283 if (FLFile
.empty() == true)
285 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
290 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
293 Sources
.Output
= 0; // Just in case
295 // Finish compressing
296 unsigned long long Size
;
297 if (Comp
.Finalize(Size
) == false)
300 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
305 << SizeToStr(Size
) << "B ";
309 struct timeval NewTime
;
310 gettimeofday(&NewTime
,0);
311 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
312 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
314 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
315 TimeToStr((long)Delta
) << endl
;
317 Stats
.Add(Sources
.Stats
);
318 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
320 return !_error
->PendingError();
323 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
324 // ---------------------------------------------------------------------
325 /* This generates the contents file partially described by this object.
326 It searches the given iterator range for other package files that map
327 into this contents file and includes their data as well when building. */
328 bool PackageMap::GenContents(Configuration
&Setup
,
329 vector
<PackageMap
>::iterator Begin
,
330 vector
<PackageMap
>::iterator End
,
333 if (Contents
.empty() == true)
339 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
340 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
341 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
343 struct timeval StartTime
;
344 gettimeofday(&StartTime
,0);
346 // Create a package writer object.
347 ContentsWriter
Contents("", Arch
);
348 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
349 return _error
->Error(_("Package extension list is too long"));
350 if (_error
->PendingError() == true)
353 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
354 CntCompress
,Permissions
);
355 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
356 Contents
.Output
= Comp
.Input
;
357 if (_error
->PendingError() == true)
360 // Write the header out.
361 if (ContentsHead
.empty() == false)
363 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
364 if (_error
->PendingError() == true)
367 unsigned long long Size
= Head
.Size();
368 unsigned char Buf
[4096];
371 unsigned long long ToRead
= Size
;
372 if (Size
> sizeof(Buf
))
373 ToRead
= sizeof(Buf
);
375 if (Head
.Read(Buf
,ToRead
) == false)
378 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
379 return _error
->Errno("fwrite",_("Error writing header to contents file"));
385 /* Go over all the package file records and parse all the package
386 files associated with this contents file into one great big honking
387 memory structure, then dump the sorted version */
388 c0out
<< ' ' << this->Contents
<< ":" << flush
;
389 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; ++I
)
391 if (I
->Contents
!= this->Contents
)
394 Contents
.Prefix
= ArchiveDir
;
395 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
396 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
399 I
->ContentsDone
= true;
404 // Finish compressing
405 unsigned long long Size
;
406 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
409 return _error
->Error(_("Error processing contents %s"),
410 this->Contents
.c_str());
415 c0out
<< " New " << SizeToStr(Size
) << "B ";
424 struct timeval NewTime
;
425 gettimeofday(&NewTime
,0);
426 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
427 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
429 c0out
<< Contents
.Stats
.Packages
<< " files " <<
430 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
431 TimeToStr((long)Delta
) << endl
;
437 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
438 // ---------------------------------------------------------------------
439 /* This populates the PkgList with all the possible permutations of the
440 section/arch lists. */
441 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
444 string DDir
= Setup
.Find("TreeDefault::Directory",
445 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
446 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
447 "$(DIST)/$(SECTION)/source/");
448 string DPkg
= Setup
.Find("TreeDefault::Packages",
449 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
450 string DTrans
= Setup
.Find("TreeDefault::Translation",
451 "$(DIST)/$(SECTION)/i18n/Translation-en");
452 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
453 "$(DIST)/$(SECTION)/");
454 string DContents
= Setup
.Find("TreeDefault::Contents",
455 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
456 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
457 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
458 "packages-$(ARCH).db");
459 string DSources
= Setup
.Find("TreeDefault::Sources",
460 "$(DIST)/$(SECTION)/source/Sources");
461 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
462 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
464 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
466 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
467 _config
->FindB("APT::FTPArchive::LongDescription", true));
468 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
470 // Process 'tree' type sections
471 const Configuration::Item
*Top
= Setup
.Tree("tree");
472 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
474 Configuration
Block(Top
);
475 string Dist
= Top
->Tag
;
477 // Parse the sections
478 string Tmp
= Block
.Find("Sections");
479 const char *Sections
= Tmp
.c_str();
481 while (ParseQuoteWord(Sections
,Section
) == true)
484 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
485 {"$(SECTION)",&Section
},
488 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
489 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
490 TranslationWriter
*TransWriter
;
491 if (DTrans
.empty() == false && LongDesc
== false)
493 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
494 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
495 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
496 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
501 string
const Tmp2
= Block
.Find("Architectures");
502 const char *Archs
= Tmp2
.c_str();
503 while (ParseQuoteWord(Archs
,Arch
) == true)
506 Itm
.Permissions
= Perms
;
507 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
508 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
510 if (stringcasecmp(Arch
,"source") == 0)
512 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
513 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
514 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
515 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
516 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
517 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
521 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
522 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
523 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
524 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
526 Itm
.LongDesc
= LongDesc
;
527 if (TransWriter
!= NULL
)
529 TransWriter
->IncreaseRefCounter();
530 Itm
.TransWriter
= TransWriter
;
532 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
533 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
534 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
535 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
538 Itm
.GetGeneral(Setup
,Block
);
539 PkgList
.push_back(Itm
);
541 // we didn't use this TransWriter, so we can release it
542 if (TransWriter
!= NULL
&& TransWriter
->GetRefCounter() == 0)
550 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
551 // ---------------------------------------------------------------------
553 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
555 mode_t
const Permissions
= Setup
.FindI("Default::FileMode",0644);
557 // Process 'bindirectory' type sections
558 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
559 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
561 Configuration
Block(Top
);
564 Itm
.PkgFile
= Block
.Find("Packages");
565 Itm
.SrcFile
= Block
.Find("Sources");
566 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
567 Itm
.BinOverride
= Block
.Find("BinOverride");
568 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
569 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
570 Itm
.SrcOverride
= Block
.Find("SrcOverride");
571 Itm
.BaseDir
= Top
->Tag
;
572 Itm
.FLFile
= Block
.Find("FileList");
573 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
574 Itm
.Contents
= Block
.Find("Contents");
575 Itm
.ContentsHead
= Block
.Find("Contents::Header");
576 Itm
.Permissions
= Block
.FindI("FileMode", Permissions
);
578 Itm
.GetGeneral(Setup
,Block
);
579 PkgList
.push_back(Itm
);
586 // ShowHelp - Show the help text /*{{{*/
587 // ---------------------------------------------------------------------
589 bool ShowHelp(CommandLine
&CmdL
)
591 ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,PACKAGE_VERSION
,
592 COMMON_ARCH
,__DATE__
,__TIME__
);
593 if (_config
->FindB("version") == true)
597 _("Usage: apt-ftparchive [options] command\n"
598 "Commands: packages binarypath [overridefile [pathprefix]]\n"
599 " sources srcpath [overridefile [pathprefix]]\n"
602 " generate config [groups]\n"
605 "apt-ftparchive generates index files for Debian archives. It supports\n"
606 "many styles of generation from fully automated to functional replacements\n"
607 "for dpkg-scanpackages and dpkg-scansources\n"
609 "apt-ftparchive generates Package files from a tree of .debs. The\n"
610 "Package file contains the contents of all the control fields from\n"
611 "each package as well as the MD5 hash and filesize. An override file\n"
612 "is supported to force the value of Priority and Section.\n"
614 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
615 "The --source-override option can be used to specify a src override file\n"
617 "The 'packages' and 'sources' command should be run in the root of the\n"
618 "tree. BinaryPath should point to the base of the recursive search and \n"
619 "override file should contain the override flags. Pathprefix is\n"
620 "appended to the filename fields if present. Example usage from the \n"
622 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
623 " dists/potato/main/binary-i386/Packages\n"
626 " -h This help text\n"
627 " --md5 Control MD5 generation\n"
628 " -s=? Source override file\n"
630 " -d=? Select the optional caching database\n"
631 " --no-delink Enable delinking debug mode\n"
632 " --contents Control contents file generation\n"
633 " -c=? Read this configuration file\n"
634 " -o=? Set an arbitrary configuration option") << endl
;
639 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
640 // ---------------------------------------------------------------------
641 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
642 bool SimpleGenPackages(CommandLine
&CmdL
)
644 if (CmdL
.FileSize() < 2)
645 return ShowHelp(CmdL
);
648 if (CmdL
.FileSize() >= 3)
649 Override
= CmdL
.FileList
[2];
651 // Create a package writer object.
652 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
653 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
654 if (_error
->PendingError() == true)
657 if (CmdL
.FileSize() >= 4)
658 Packages
.PathPrefix
= CmdL
.FileList
[3];
660 // Do recursive directory searching
661 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
667 // SimpleGenContents - Generate a Contents listing /*{{{*/
668 // ---------------------------------------------------------------------
670 bool SimpleGenContents(CommandLine
&CmdL
)
672 if (CmdL
.FileSize() < 2)
673 return ShowHelp(CmdL
);
675 // Create a package writer object.
676 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
677 if (_error
->PendingError() == true)
680 // Do recursive directory searching
681 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
689 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
690 // ---------------------------------------------------------------------
691 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
692 bool SimpleGenSources(CommandLine
&CmdL
)
694 if (CmdL
.FileSize() < 2)
695 return ShowHelp(CmdL
);
698 if (CmdL
.FileSize() >= 3)
699 Override
= CmdL
.FileList
[2];
702 if (Override
.empty() == false)
703 SOverride
= Override
+ ".src";
705 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
708 // Create a package writer object.
709 SourcesWriter
Sources(_config
->Find("APT::FTPArchive::DB"),Override
,SOverride
);
710 if (_error
->PendingError() == true)
713 if (CmdL
.FileSize() >= 4)
714 Sources
.PathPrefix
= CmdL
.FileList
[3];
716 // Do recursive directory searching
717 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
723 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
724 // ---------------------------------------------------------------------
725 bool SimpleGenRelease(CommandLine
&CmdL
)
727 if (CmdL
.FileSize() < 2)
728 return ShowHelp(CmdL
);
730 string Dir
= CmdL
.FileList
[1];
732 ReleaseWriter
Release("");
733 Release
.DirStrip
= Dir
;
735 if (_error
->PendingError() == true)
738 if (Release
.RecursiveScan(Dir
) == false)
747 // Generate - Full generate, using a config file /*{{{*/
748 // ---------------------------------------------------------------------
750 bool Generate(CommandLine
&CmdL
)
752 struct CacheDB::Stats SrcStats
;
753 if (CmdL
.FileSize() < 2)
754 return ShowHelp(CmdL
);
756 struct timeval StartTime
;
757 gettimeofday(&StartTime
,0);
758 struct CacheDB::Stats Stats
;
760 // Read the configuration file.
762 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
765 vector
<PackageMap
> PkgList
;
766 LoadTree(PkgList
,Setup
);
767 LoadBinDir(PkgList
,Setup
);
769 // Sort by cache DB to improve IO locality.
770 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
773 if (CmdL
.FileSize() <= 2)
775 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
776 if (I
->GenPackages(Setup
,Stats
) == false)
777 _error
->DumpErrors();
778 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
779 if (I
->GenSources(Setup
,SrcStats
) == false)
780 _error
->DumpErrors();
784 // Make a choice list out of the package list..
785 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
786 RxChoiceList
*End
= List
;
787 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
789 End
->UserData
= &(*I
);
790 End
->Str
= I
->BaseDir
.c_str();
793 End
->UserData
= &(*I
);
794 End
->Str
= I
->Tag
.c_str();
800 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
803 return _error
->Error(_("No selections matched"));
805 _error
->DumpErrors();
807 // Do the generation for Packages
808 for (End
= List
; End
->Str
!= 0; End
++)
810 if (End
->Hit
== false)
813 PackageMap
*I
= (PackageMap
*)End
->UserData
;
814 if (I
->PkgDone
== true)
816 if (I
->GenPackages(Setup
,Stats
) == false)
817 _error
->DumpErrors();
820 // Do the generation for Sources
821 for (End
= List
; End
->Str
!= 0; End
++)
823 if (End
->Hit
== false)
826 PackageMap
*I
= (PackageMap
*)End
->UserData
;
827 if (I
->SrcDone
== true)
829 if (I
->GenSources(Setup
,SrcStats
) == false)
830 _error
->DumpErrors();
836 // close the Translation master files
837 for (vector
<PackageMap
>::reverse_iterator I
= PkgList
.rbegin(); I
!= PkgList
.rend(); ++I
)
838 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
839 delete I
->TransWriter
;
841 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
844 c1out
<< "Packages done, Starting contents." << endl
;
846 // Sort the contents file list by date
847 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
848 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
851 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
852 I
->CntCompress
,A
) == false)
853 time(&I
->ContentsMTime
);
855 I
->ContentsMTime
= A
.st_mtime
;
857 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
859 /* Now for Contents.. The process here is to do a make-like dependency
860 check. Each contents file is verified to be newer than the package files
861 that describe the debs it indexes. Since the package files contain
862 hashes of the .debs this means they have not changed either so the
863 contents must be up to date. */
864 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
865 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); ++I
)
867 // This record is not relevant
868 if (I
->ContentsDone
== true ||
869 I
->Contents
.empty() == true)
872 // Do not do everything if the user specified sections.
873 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
877 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
879 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
881 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
885 if (A
.st_mtime
> B
.st_mtime
)
889 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
890 MaxContentsChange
) == false)
891 _error
->DumpErrors();
894 if (MaxContentsChange
== 0)
896 c1out
<< "Hit contents update byte limit" << endl
;
901 struct timeval NewTime
;
902 gettimeofday(&NewTime
,0);
903 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
904 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
905 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
906 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
911 // Clean - Clean out the databases /*{{{*/
912 // ---------------------------------------------------------------------
914 bool Clean(CommandLine
&CmdL
)
916 if (CmdL
.FileSize() != 2)
917 return ShowHelp(CmdL
);
919 // Read the configuration file.
921 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
924 vector
<PackageMap
> PkgList
;
925 LoadTree(PkgList
,Setup
);
926 LoadBinDir(PkgList
,Setup
);
928 // Sort by cache DB to improve IO locality.
929 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
931 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
933 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
935 c0out
<< I
->BinCacheDB
<< endl
;
936 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
937 if (DB
.Clean() == false)
938 _error
->DumpErrors();
940 string CacheDB
= I
->BinCacheDB
;
941 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; ++I
);
948 int main(int argc
, const char *argv
[])
950 setlocale(LC_ALL
, "");
951 CommandLine::Args Args
[] = {
952 {'h',"help","help",0},
953 {0,"md5","APT::FTPArchive::MD5",0},
954 {0,"sha1","APT::FTPArchive::SHA1",0},
955 {0,"sha256","APT::FTPArchive::SHA256",0},
956 {'v',"version","version",0},
957 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
958 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
959 {'q',"quiet","quiet",CommandLine::IntLevel
},
960 {'q',"silent","quiet",CommandLine::IntLevel
},
961 {0,"delink","APT::FTPArchive::DeLinkAct",0},
962 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
963 {0,"contents","APT::FTPArchive::Contents",0},
964 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
965 {'c',"config-file",0,CommandLine::ConfigFile
},
966 {'o',"option",0,CommandLine::ArbItem
},
968 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
969 {"contents",&SimpleGenContents
},
970 {"sources",&SimpleGenSources
},
971 {"release",&SimpleGenRelease
},
972 {"generate",&Generate
},
977 // Parse the command line and initialize the package library
978 CommandLine
CmdL(Args
,_config
);
979 if (pkgInitConfig(*_config
) == false || CmdL
.Parse(argc
,argv
) == false)
981 _error
->DumpErrors();
985 // See if the help should be shown
986 if (_config
->FindB("help") == true ||
987 _config
->FindB("version") == true ||
988 CmdL
.FileSize() == 0)
994 // Setup the output streams
995 c0out
.rdbuf(clog
.rdbuf());
996 c1out
.rdbuf(clog
.rdbuf());
997 c2out
.rdbuf(clog
.rdbuf());
998 Quiet
= _config
->FindI("quiet",0);
1000 c0out
.rdbuf(devnull
.rdbuf());
1002 c1out
.rdbuf(devnull
.rdbuf());
1004 // Match the operation
1005 CmdL
.DispatchArg(Cmds
);
1007 if (_error
->empty() == false)
1009 bool Errors
= _error
->PendingError();
1010 _error
->DumpErrors();
1011 return Errors
== true?100:0;