]>
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 /*{{{*/
13 #include "apt-ftparchive.h"
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>
29 #include "multicompress.h"
37 ofstream
devnull("/dev/null");
40 // struct PackageMap - List of all package files in the config file /*{{{*/
41 // ---------------------------------------------------------------------
47 string InternalPrefix
;
52 // Stuff for the Package File
58 // We generate for this given arch
61 // Stuff for the Source File
64 string SrcExtraOverride
;
66 // Translation master file
68 TranslationWriter
*TransWriter
;
80 unsigned int DeLinkLimit
;
88 struct ContentsCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
90 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
91 {return x
.ContentsMTime
< y
.ContentsMTime
;};
94 struct DBCompare
: public binary_function
<PackageMap
,PackageMap
,bool>
96 inline bool operator() (const PackageMap
&x
,const PackageMap
&y
)
97 {return x
.BinCacheDB
< y
.BinCacheDB
;};
100 void GetGeneral(Configuration
&Setup
,Configuration
&Block
);
101 bool GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
102 bool GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
);
103 bool GenContents(Configuration
&Setup
,
104 vector
<PackageMap
>::iterator Begin
,
105 vector
<PackageMap
>::iterator End
,
106 unsigned long &Left
);
108 PackageMap() : LongDesc(true), TransWriter(NULL
), DeLinkLimit(0), Permissions(1),
109 ContentsDone(false), PkgDone(false), SrcDone(false),
114 // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
115 // ---------------------------------------------------------------------
117 void PackageMap::GetGeneral(Configuration
&Setup
,Configuration
&Block
)
119 PathPrefix
= Block
.Find("PathPrefix");
121 if (Block
.FindB("External-Links",true) == false)
122 DeLinkLimit
= Setup
.FindI("Default::DeLinkLimit",UINT_MAX
);
126 PkgCompress
= Block
.Find("Packages::Compress",
127 Setup
.Find("Default::Packages::Compress",". gzip").c_str());
128 CntCompress
= Block
.Find("Contents::Compress",
129 Setup
.Find("Default::Contents::Compress",". gzip").c_str());
130 SrcCompress
= Block
.Find("Sources::Compress",
131 Setup
.Find("Default::Sources::Compress",". gzip").c_str());
133 SrcExt
= Block
.Find("Sources::Extensions",
134 Setup
.Find("Default::Sources::Extensions",".dsc").c_str());
135 PkgExt
= Block
.Find("Packages::Extensions",
136 Setup
.Find("Default::Packages::Extensions",".deb").c_str());
138 if (FLFile
.empty() == false)
139 FLFile
= flCombine(Setup
.Find("Dir::FileListDir"),FLFile
);
145 // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
146 // ---------------------------------------------------------------------
147 /* This generates the Package File described by this object. */
148 bool PackageMap::GenPackages(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
150 if (PkgFile
.empty() == true)
153 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
154 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
155 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
157 struct timeval StartTime
;
158 gettimeofday(&StartTime
,0);
162 // Create a package writer object.
163 PackagesWriter
Packages(flCombine(CacheDir
,BinCacheDB
),
164 flCombine(OverrideDir
,BinOverride
),
165 flCombine(OverrideDir
,ExtraOverride
),
167 if (PkgExt
.empty() == false && Packages
.SetExts(PkgExt
) == false)
168 return _error
->Error(_("Package extension list is too long"));
169 if (_error
->PendingError() == true)
170 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
172 Packages
.PathPrefix
= PathPrefix
;
173 Packages
.DirStrip
= ArchiveDir
;
174 Packages
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
176 Packages
.TransWriter
= TransWriter
;
177 Packages
.LongDescription
= LongDesc
;
179 Packages
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
180 Packages
.DeLinkLimit
= DeLinkLimit
;
182 // Create a compressor object
183 MultiCompress
Comp(flCombine(ArchiveDir
,PkgFile
),
184 PkgCompress
,Permissions
);
185 Packages
.Output
= Comp
.Input
;
186 if (_error
->PendingError() == true)
187 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
189 c0out
<< ' ' << BaseDir
<< ":" << flush
;
191 // Do recursive directory searching
192 if (FLFile
.empty() == true)
194 if (Packages
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
)) == false)
199 if (Packages
.LoadFileList(ArchiveDir
,FLFile
) == false)
203 Packages
.Output
= 0; // Just in case
205 // Finish compressing
207 if (Comp
.Finalize(Size
) == false)
210 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
215 << SizeToStr(Size
) << "B ";
219 struct timeval NewTime
;
220 gettimeofday(&NewTime
,0);
221 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
222 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
224 c0out
<< Packages
.Stats
.Packages
<< " files " <<
225 /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
226 SizeToStr(Packages
.Stats
.Bytes
) << "B " <<
227 TimeToStr((long)Delta
) << endl
;
229 Stats
.Add(Packages
.Stats
);
230 Stats
.DeLinkBytes
= Packages
.Stats
.DeLinkBytes
;
232 return !_error
->PendingError();
236 // PackageMap::GenSources - Actually generate a Source file /*{{{*/
237 // ---------------------------------------------------------------------
238 /* This generates the Sources File described by this object. */
239 bool PackageMap::GenSources(Configuration
&Setup
,struct CacheDB::Stats
&Stats
)
241 if (SrcFile
.empty() == true)
244 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
245 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
246 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
248 struct timeval StartTime
;
249 gettimeofday(&StartTime
,0);
253 // Create a package writer object.
254 SourcesWriter
Sources(flCombine(OverrideDir
,BinOverride
),
255 flCombine(OverrideDir
,SrcOverride
),
256 flCombine(OverrideDir
,SrcExtraOverride
));
257 if (SrcExt
.empty() == false && Sources
.SetExts(SrcExt
) == false)
258 return _error
->Error(_("Source extension list is too long"));
259 if (_error
->PendingError() == true)
260 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
262 Sources
.PathPrefix
= PathPrefix
;
263 Sources
.DirStrip
= ArchiveDir
;
264 Sources
.InternalPrefix
= flCombine(ArchiveDir
,InternalPrefix
);
266 Sources
.DeLinkLimit
= DeLinkLimit
;
267 Sources
.Stats
.DeLinkBytes
= Stats
.DeLinkBytes
;
269 // Create a compressor object
270 MultiCompress
Comp(flCombine(ArchiveDir
,SrcFile
),
271 SrcCompress
,Permissions
);
272 Sources
.Output
= Comp
.Input
;
273 if (_error
->PendingError() == true)
274 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
276 c0out
<< ' ' << BaseDir
<< ":" << flush
;
278 // Do recursive directory searching
279 if (FLFile
.empty() == true)
281 if (Sources
.RecursiveScan(flCombine(ArchiveDir
,BaseDir
))== false)
286 if (Sources
.LoadFileList(ArchiveDir
,FLFile
) == false)
289 Sources
.Output
= 0; // Just in case
291 // Finish compressing
293 if (Comp
.Finalize(Size
) == false)
296 return _error
->Error(_("Error processing directory %s"),BaseDir
.c_str());
301 << SizeToStr(Size
) << "B ";
305 struct timeval NewTime
;
306 gettimeofday(&NewTime
,0);
307 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
308 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
310 c0out
<< Sources
.Stats
.Packages
<< " pkgs in " <<
311 TimeToStr((long)Delta
) << endl
;
313 Stats
.Add(Sources
.Stats
);
314 Stats
.DeLinkBytes
= Sources
.Stats
.DeLinkBytes
;
316 return !_error
->PendingError();
319 // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
320 // ---------------------------------------------------------------------
321 /* This generates the contents file partially described by this object.
322 It searches the given iterator range for other package files that map
323 into this contents file and includes their data as well when building. */
324 bool PackageMap::GenContents(Configuration
&Setup
,
325 vector
<PackageMap
>::iterator Begin
,
326 vector
<PackageMap
>::iterator End
,
329 if (Contents
.empty() == true)
335 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
336 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
337 string OverrideDir
= Setup
.FindDir("Dir::OverrideDir");
339 struct timeval StartTime
;
340 gettimeofday(&StartTime
,0);
342 // Create a package writer object.
343 ContentsWriter
Contents("", Arch
);
344 if (PkgExt
.empty() == false && Contents
.SetExts(PkgExt
) == false)
345 return _error
->Error(_("Package extension list is too long"));
346 if (_error
->PendingError() == true)
349 MultiCompress
Comp(flCombine(ArchiveDir
,this->Contents
),
350 CntCompress
,Permissions
);
351 Comp
.UpdateMTime
= Setup
.FindI("Default::ContentsAge",10)*24*60*60;
352 Contents
.Output
= Comp
.Input
;
353 if (_error
->PendingError() == true)
356 // Write the header out.
357 if (ContentsHead
.empty() == false)
359 FileFd
Head(flCombine(OverrideDir
,ContentsHead
),FileFd::ReadOnly
);
360 if (_error
->PendingError() == true)
363 unsigned long Size
= Head
.Size();
364 unsigned char Buf
[4096];
367 unsigned long ToRead
= Size
;
368 if (Size
> sizeof(Buf
))
369 ToRead
= sizeof(Buf
);
371 if (Head
.Read(Buf
,ToRead
) == false)
374 if (fwrite(Buf
,1,ToRead
,Comp
.Input
) != ToRead
)
375 return _error
->Errno("fwrite",_("Error writing header to contents file"));
381 /* Go over all the package file records and parse all the package
382 files associated with this contents file into one great big honking
383 memory structure, then dump the sorted version */
384 c0out
<< ' ' << this->Contents
<< ":" << flush
;
385 for (vector
<PackageMap
>::iterator I
= Begin
; I
!= End
; I
++)
387 if (I
->Contents
!= this->Contents
)
390 Contents
.Prefix
= ArchiveDir
;
391 Contents
.ReadyDB(flCombine(CacheDir
,I
->BinCacheDB
));
392 Contents
.ReadFromPkgs(flCombine(ArchiveDir
,I
->PkgFile
),
395 I
->ContentsDone
= true;
400 // Finish compressing
402 if (Comp
.Finalize(Size
) == false || _error
->PendingError() == true)
405 return _error
->Error(_("Error processing contents %s"),
406 this->Contents
.c_str());
411 c0out
<< " New " << SizeToStr(Size
) << "B ";
420 struct timeval NewTime
;
421 gettimeofday(&NewTime
,0);
422 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
423 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
425 c0out
<< Contents
.Stats
.Packages
<< " files " <<
426 SizeToStr(Contents
.Stats
.Bytes
) << "B " <<
427 TimeToStr((long)Delta
) << endl
;
433 // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
434 // ---------------------------------------------------------------------
435 /* This populates the PkgList with all the possible permutations of the
436 section/arch lists. */
437 void LoadTree(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
440 string DDir
= Setup
.Find("TreeDefault::Directory",
441 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
442 string DSDir
= Setup
.Find("TreeDefault::SrcDirectory",
443 "$(DIST)/$(SECTION)/source/");
444 string DPkg
= Setup
.Find("TreeDefault::Packages",
445 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
446 string DTrans
= Setup
.Find("TreeDefault::Translation",
447 "$(DIST)/$(SECTION)/i18n/Translation-en");
448 string DIPrfx
= Setup
.Find("TreeDefault::InternalPrefix",
449 "$(DIST)/$(SECTION)/");
450 string DContents
= Setup
.Find("TreeDefault::Contents",
451 "$(DIST)/Contents-$(ARCH)");
452 string DContentsH
= Setup
.Find("TreeDefault::Contents::Header","");
453 string DBCache
= Setup
.Find("TreeDefault::BinCacheDB",
454 "packages-$(ARCH).db");
455 string DSources
= Setup
.Find("TreeDefault::Sources",
456 "$(DIST)/$(SECTION)/source/Sources");
457 string DFLFile
= Setup
.Find("TreeDefault::FileList", "");
458 string DSFLFile
= Setup
.Find("TreeDefault::SourceFileList", "");
460 int const Permissions
= Setup
.FindI("Default::FileMode",0644);
462 bool const LongDescription
= Setup
.FindB("Default::LongDescription",
463 _config
->FindB("APT::FTPArchive::LongDescription", true));
464 string
const TranslationCompress
= Setup
.Find("Default::Translation::Compress",". gzip").c_str();
466 // Process 'tree' type sections
467 const Configuration::Item
*Top
= Setup
.Tree("tree");
468 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
470 Configuration
Block(Top
);
471 string Dist
= Top
->Tag
;
473 // Parse the sections
474 string Tmp
= Block
.Find("Sections");
475 const char *Sections
= Tmp
.c_str();
477 while (ParseQuoteWord(Sections
,Section
) == true)
480 struct SubstVar
const Vars
[] = {{"$(DIST)",&Dist
},
481 {"$(SECTION)",&Section
},
484 mode_t
const Perms
= Block
.FindI("FileMode", Permissions
);
485 bool const LongDesc
= Block
.FindB("LongDescription", LongDescription
);
486 TranslationWriter
*TransWriter
;
487 if (DTrans
.empty() == false && LongDesc
== false)
489 string
const TranslationFile
= flCombine(Setup
.FindDir("Dir::ArchiveDir"),
490 SubstVar(Block
.Find("Translation", DTrans
.c_str()), Vars
));
491 string
const TransCompress
= Block
.Find("Translation::Compress", TranslationCompress
);
492 TransWriter
= new TranslationWriter(TranslationFile
, TransCompress
, Perms
);
497 string
const Tmp2
= Block
.Find("Architectures");
498 const char *Archs
= Tmp2
.c_str();
499 while (ParseQuoteWord(Archs
,Arch
) == true)
502 Itm
.Permissions
= Perms
;
503 Itm
.BinOverride
= SubstVar(Block
.Find("BinOverride"),Vars
);
504 Itm
.InternalPrefix
= SubstVar(Block
.Find("InternalPrefix",DIPrfx
.c_str()),Vars
);
506 if (stringcasecmp(Arch
,"source") == 0)
508 Itm
.SrcOverride
= SubstVar(Block
.Find("SrcOverride"),Vars
);
509 Itm
.BaseDir
= SubstVar(Block
.Find("SrcDirectory",DSDir
.c_str()),Vars
);
510 Itm
.SrcFile
= SubstVar(Block
.Find("Sources",DSources
.c_str()),Vars
);
511 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/source",Vars
);
512 Itm
.FLFile
= SubstVar(Block
.Find("SourceFileList",DSFLFile
.c_str()),Vars
);
513 Itm
.SrcExtraOverride
= SubstVar(Block
.Find("SrcExtraOverride"),Vars
);
517 Itm
.BinCacheDB
= SubstVar(Block
.Find("BinCacheDB",DBCache
.c_str()),Vars
);
518 Itm
.BaseDir
= SubstVar(Block
.Find("Directory",DDir
.c_str()),Vars
);
519 Itm
.PkgFile
= SubstVar(Block
.Find("Packages",DPkg
.c_str()),Vars
);
520 Itm
.Tag
= SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars
);
522 Itm
.LongDesc
= LongDesc
;
523 if (TransWriter
!= NULL
)
525 TransWriter
->IncreaseRefCounter();
526 Itm
.TransWriter
= TransWriter
;
528 Itm
.Contents
= SubstVar(Block
.Find("Contents",DContents
.c_str()),Vars
);
529 Itm
.ContentsHead
= SubstVar(Block
.Find("Contents::Header",DContentsH
.c_str()),Vars
);
530 Itm
.FLFile
= SubstVar(Block
.Find("FileList",DFLFile
.c_str()),Vars
);
531 Itm
.ExtraOverride
= SubstVar(Block
.Find("ExtraOverride"),Vars
);
534 Itm
.GetGeneral(Setup
,Block
);
535 PkgList
.push_back(Itm
);
537 // we didn't use this TransWriter, so we can release it
538 if (TransWriter
!= NULL
&& TransWriter
->GetRefCounter() == 0)
546 // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
547 // ---------------------------------------------------------------------
549 void LoadBinDir(vector
<PackageMap
> &PkgList
,Configuration
&Setup
)
551 // Process 'bindirectory' type sections
552 const Configuration::Item
*Top
= Setup
.Tree("bindirectory");
553 for (Top
= (Top
== 0?0:Top
->Child
); Top
!= 0;)
555 Configuration
Block(Top
);
558 Itm
.PkgFile
= Block
.Find("Packages");
559 Itm
.SrcFile
= Block
.Find("Sources");
560 Itm
.BinCacheDB
= Block
.Find("BinCacheDB");
561 Itm
.BinOverride
= Block
.Find("BinOverride");
562 Itm
.ExtraOverride
= Block
.Find("ExtraOverride");
563 Itm
.SrcExtraOverride
= Block
.Find("SrcExtraOverride");
564 Itm
.SrcOverride
= Block
.Find("SrcOverride");
565 Itm
.BaseDir
= Top
->Tag
;
566 Itm
.FLFile
= Block
.Find("FileList");
567 Itm
.InternalPrefix
= Block
.Find("InternalPrefix",Top
->Tag
.c_str());
568 Itm
.Contents
= Block
.Find("Contents");
569 Itm
.ContentsHead
= Block
.Find("Contents::Header");
571 Itm
.GetGeneral(Setup
,Block
);
572 PkgList
.push_back(Itm
);
579 // ShowHelp - Show the help text /*{{{*/
580 // ---------------------------------------------------------------------
582 bool ShowHelp(CommandLine
&CmdL
)
584 ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,VERSION
,
585 COMMON_ARCH
,__DATE__
,__TIME__
);
586 if (_config
->FindB("version") == true)
590 _("Usage: apt-ftparchive [options] command\n"
591 "Commands: packages binarypath [overridefile [pathprefix]]\n"
592 " sources srcpath [overridefile [pathprefix]]\n"
595 " generate config [groups]\n"
598 "apt-ftparchive generates index files for Debian archives. It supports\n"
599 "many styles of generation from fully automated to functional replacements\n"
600 "for dpkg-scanpackages and dpkg-scansources\n"
602 "apt-ftparchive generates Package files from a tree of .debs. The\n"
603 "Package file contains the contents of all the control fields from\n"
604 "each package as well as the MD5 hash and filesize. An override file\n"
605 "is supported to force the value of Priority and Section.\n"
607 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
608 "The --source-override option can be used to specify a src override file\n"
610 "The 'packages' and 'sources' command should be run in the root of the\n"
611 "tree. BinaryPath should point to the base of the recursive search and \n"
612 "override file should contain the override flags. Pathprefix is\n"
613 "appended to the filename fields if present. Example usage from the \n"
615 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
616 " dists/potato/main/binary-i386/Packages\n"
619 " -h This help text\n"
620 " --md5 Control MD5 generation\n"
621 " -s=? Source override file\n"
623 " -d=? Select the optional caching database\n"
624 " --no-delink Enable delinking debug mode\n"
625 " --contents Control contents file generation\n"
626 " -c=? Read this configuration file\n"
627 " -o=? Set an arbitrary configuration option") << endl
;
632 // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
633 // ---------------------------------------------------------------------
634 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
635 bool SimpleGenPackages(CommandLine
&CmdL
)
637 if (CmdL
.FileSize() < 2)
638 return ShowHelp(CmdL
);
641 if (CmdL
.FileSize() >= 3)
642 Override
= CmdL
.FileList
[2];
644 // Create a package writer object.
645 PackagesWriter
Packages(_config
->Find("APT::FTPArchive::DB"),
646 Override
, "", _config
->Find("APT::FTPArchive::Architecture"));
647 if (_error
->PendingError() == true)
650 if (CmdL
.FileSize() >= 4)
651 Packages
.PathPrefix
= CmdL
.FileList
[3];
653 // Do recursive directory searching
654 if (Packages
.RecursiveScan(CmdL
.FileList
[1]) == false)
660 // SimpleGenContents - Generate a Contents listing /*{{{*/
661 // ---------------------------------------------------------------------
663 bool SimpleGenContents(CommandLine
&CmdL
)
665 if (CmdL
.FileSize() < 2)
666 return ShowHelp(CmdL
);
668 // Create a package writer object.
669 ContentsWriter
Contents(_config
->Find("APT::FTPArchive::DB"), _config
->Find("APT::FTPArchive::Architecture"));
670 if (_error
->PendingError() == true)
673 // Do recursive directory searching
674 if (Contents
.RecursiveScan(CmdL
.FileList
[1]) == false)
682 // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
683 // ---------------------------------------------------------------------
684 /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
685 bool SimpleGenSources(CommandLine
&CmdL
)
687 if (CmdL
.FileSize() < 2)
688 return ShowHelp(CmdL
);
691 if (CmdL
.FileSize() >= 3)
692 Override
= CmdL
.FileList
[2];
695 if (Override
.empty() == false)
696 SOverride
= Override
+ ".src";
698 SOverride
= _config
->Find("APT::FTPArchive::SourceOverride",
701 // Create a package writer object.
702 SourcesWriter
Sources(Override
,SOverride
);
703 if (_error
->PendingError() == true)
706 if (CmdL
.FileSize() >= 4)
707 Sources
.PathPrefix
= CmdL
.FileList
[3];
709 // Do recursive directory searching
710 if (Sources
.RecursiveScan(CmdL
.FileList
[1]) == false)
716 // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
717 // ---------------------------------------------------------------------
718 bool SimpleGenRelease(CommandLine
&CmdL
)
720 if (CmdL
.FileSize() < 2)
721 return ShowHelp(CmdL
);
723 string Dir
= CmdL
.FileList
[1];
725 ReleaseWriter
Release("");
726 Release
.DirStrip
= Dir
;
728 if (_error
->PendingError() == true)
731 if (Release
.RecursiveScan(Dir
) == false)
740 // Generate - Full generate, using a config file /*{{{*/
741 // ---------------------------------------------------------------------
743 bool Generate(CommandLine
&CmdL
)
745 struct CacheDB::Stats SrcStats
;
746 if (CmdL
.FileSize() < 2)
747 return ShowHelp(CmdL
);
749 struct timeval StartTime
;
750 gettimeofday(&StartTime
,0);
751 struct CacheDB::Stats Stats
;
753 // Read the configuration file.
755 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
758 vector
<PackageMap
> PkgList
;
759 LoadTree(PkgList
,Setup
);
760 LoadBinDir(PkgList
,Setup
);
762 // Sort by cache DB to improve IO locality.
763 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
766 if (CmdL
.FileSize() <= 2)
768 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
769 if (I
->GenPackages(Setup
,Stats
) == false)
770 _error
->DumpErrors();
771 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
772 if (I
->GenSources(Setup
,SrcStats
) == false)
773 _error
->DumpErrors();
777 // Make a choice list out of the package list..
778 RxChoiceList
*List
= new RxChoiceList
[2*PkgList
.size()+1];
779 RxChoiceList
*End
= List
;
780 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
782 End
->UserData
= &(*I
);
783 End
->Str
= I
->BaseDir
.c_str();
786 End
->UserData
= &(*I
);
787 End
->Str
= I
->Tag
.c_str();
793 if (RegexChoice(List
,CmdL
.FileList
+ 2,CmdL
.FileList
+ CmdL
.FileSize()) == 0)
796 return _error
->Error(_("No selections matched"));
798 _error
->DumpErrors();
800 // Do the generation for Packages
801 for (End
= List
; End
->Str
!= 0; End
++)
803 if (End
->Hit
== false)
806 PackageMap
*I
= (PackageMap
*)End
->UserData
;
807 if (I
->PkgDone
== true)
809 if (I
->GenPackages(Setup
,Stats
) == false)
810 _error
->DumpErrors();
813 // Do the generation for Sources
814 for (End
= List
; End
->Str
!= 0; End
++)
816 if (End
->Hit
== false)
819 PackageMap
*I
= (PackageMap
*)End
->UserData
;
820 if (I
->SrcDone
== true)
822 if (I
->GenSources(Setup
,SrcStats
) == false)
823 _error
->DumpErrors();
829 // close the Translation master files
830 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
831 if (I
->TransWriter
!= NULL
&& I
->TransWriter
->DecreaseRefCounter() == 0)
832 delete I
->TransWriter
;
834 if (_config
->FindB("APT::FTPArchive::Contents",true) == false)
837 c1out
<< "Packages done, Starting contents." << endl
;
839 // Sort the contents file list by date
840 string ArchiveDir
= Setup
.FindDir("Dir::ArchiveDir");
841 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
844 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),
845 I
->CntCompress
,A
) == false)
846 time(&I
->ContentsMTime
);
848 I
->ContentsMTime
= A
.st_mtime
;
850 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::ContentsCompare());
852 /* Now for Contents.. The process here is to do a make-like dependency
853 check. Each contents file is verified to be newer than the package files
854 that describe the debs it indexes. Since the package files contain
855 hashes of the .debs this means they have not changed either so the
856 contents must be up to date. */
857 unsigned long MaxContentsChange
= Setup
.FindI("Default::MaxContentsChange",UINT_MAX
)*1024;
858 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); I
++)
860 // This record is not relevent
861 if (I
->ContentsDone
== true ||
862 I
->Contents
.empty() == true)
865 // Do not do everything if the user specified sections.
866 if (CmdL
.FileSize() > 2 && I
->PkgDone
== false)
870 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->Contents
),I
->CntCompress
,A
) == true)
872 if (MultiCompress::GetStat(flCombine(ArchiveDir
,I
->PkgFile
),I
->PkgCompress
,B
) == false)
874 _error
->Warning(_("Some files are missing in the package file group `%s'"),I
->PkgFile
.c_str());
878 if (A
.st_mtime
> B
.st_mtime
)
882 if (I
->GenContents(Setup
,PkgList
.begin(),PkgList
.end(),
883 MaxContentsChange
) == false)
884 _error
->DumpErrors();
887 if (MaxContentsChange
== 0)
889 c1out
<< "Hit contents update byte limit" << endl
;
894 struct timeval NewTime
;
895 gettimeofday(&NewTime
,0);
896 double Delta
= NewTime
.tv_sec
- StartTime
.tv_sec
+
897 (NewTime
.tv_usec
- StartTime
.tv_usec
)/1000000.0;
898 c1out
<< "Done. " << SizeToStr(Stats
.Bytes
) << "B in " << Stats
.Packages
899 << " archives. Took " << TimeToStr((long)Delta
) << endl
;
904 // Clean - Clean out the databases /*{{{*/
905 // ---------------------------------------------------------------------
907 bool Clean(CommandLine
&CmdL
)
909 if (CmdL
.FileSize() != 2)
910 return ShowHelp(CmdL
);
912 // Read the configuration file.
914 if (ReadConfigFile(Setup
,CmdL
.FileList
[1],true) == false)
917 vector
<PackageMap
> PkgList
;
918 LoadTree(PkgList
,Setup
);
919 LoadBinDir(PkgList
,Setup
);
921 // Sort by cache DB to improve IO locality.
922 stable_sort(PkgList
.begin(),PkgList
.end(),PackageMap::DBCompare());
924 string CacheDir
= Setup
.FindDir("Dir::CacheDir");
926 for (vector
<PackageMap
>::iterator I
= PkgList
.begin(); I
!= PkgList
.end(); )
928 c0out
<< I
->BinCacheDB
<< endl
;
929 CacheDB
DB(flCombine(CacheDir
,I
->BinCacheDB
));
930 if (DB
.Clean() == false)
931 _error
->DumpErrors();
933 string CacheDB
= I
->BinCacheDB
;
934 for (; I
!= PkgList
.end() && I
->BinCacheDB
== CacheDB
; I
++);
941 int main(int argc
, const char *argv
[])
943 setlocale(LC_ALL
, "");
944 CommandLine::Args Args
[] = {
945 {'h',"help","help",0},
946 {0,"md5","APT::FTPArchive::MD5",0},
947 {'v',"version","version",0},
948 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg
},
949 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg
},
950 {'q',"quiet","quiet",CommandLine::IntLevel
},
951 {'q',"silent","quiet",CommandLine::IntLevel
},
952 {0,"delink","APT::FTPArchive::DeLinkAct",0},
953 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
954 {0,"contents","APT::FTPArchive::Contents",0},
955 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg
},
956 {'c',"config-file",0,CommandLine::ConfigFile
},
957 {'o',"option",0,CommandLine::ArbItem
},
959 CommandLine::Dispatch Cmds
[] = {{"packages",&SimpleGenPackages
},
960 {"contents",&SimpleGenContents
},
961 {"sources",&SimpleGenSources
},
962 {"release",&SimpleGenRelease
},
963 {"generate",&Generate
},
968 // Parse the command line and initialize the package library
969 CommandLine
CmdL(Args
,_config
);
970 if (pkgInitConfig(*_config
) == false || CmdL
.Parse(argc
,argv
) == false)
972 _error
->DumpErrors();
976 // See if the help should be shown
977 if (_config
->FindB("help") == true ||
978 _config
->FindB("version") == true ||
979 CmdL
.FileSize() == 0)
985 // Setup the output streams
986 c0out
.rdbuf(clog
.rdbuf());
987 c1out
.rdbuf(clog
.rdbuf());
988 c2out
.rdbuf(clog
.rdbuf());
989 Quiet
= _config
->FindI("quiet",0);
991 c0out
.rdbuf(devnull
.rdbuf());
993 c1out
.rdbuf(devnull
.rdbuf());
995 // Match the operation
996 CmdL
.DispatchArg(Cmds
);
998 if (_error
->empty() == false)
1000 bool Errors
= _error
->PendingError();
1001 _error
->DumpErrors();
1002 return Errors
== true?100:0;