]> git.saurik.com Git - apt.git/blame - ftparchive/apt-ftparchive.cc
deduplicate main methods
[apt.git] / ftparchive / apt-ftparchive.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7db98ffc 3// $Id: apt-ftparchive.cc,v 1.8.2.3 2004/01/02 22:01:48 mdz Exp $
b2e465d6
AL
4/* ######################################################################
5
c6474fb6 6 apt-ftparchive - Efficient work-alike for dpkg-scanpackages
b2e465d6
AL
7
8 Let contents be disabled from the conf
9
10 ##################################################################### */
11 /*}}}*/
12// Include Files /*{{{*/
ea542140
DK
13#include <config.h>
14
b2e465d6
AL
15#include <apt-pkg/error.h>
16#include <apt-pkg/configuration.h>
17#include <apt-pkg/cmndline.h>
18#include <apt-pkg/strutl.h>
3b1fffc3 19#include <apt-pkg/init.h>
453b82a3 20#include <apt-pkg/fileutl.h>
b2e465d6 21
ad7e0941 22#include <apt-private/private-cmndline.h>
d9e518c6 23#include <apt-private/private-output.h>
e7e10e47 24#include <apt-private/private-main.h>
ad7e0941 25
453b82a3 26#include <algorithm>
152ab79e 27#include <climits>
b2e465d6 28#include <sys/time.h>
453b82a3
DK
29#include <locale.h>
30#include <stdio.h>
31#include <sys/stat.h>
32#include <time.h>
33#include <functional>
34#include <iostream>
35#include <string>
36#include <vector>
b2e465d6 37
453b82a3
DK
38#include "cachedb.h"
39#include "override.h"
ea542140 40#include "apt-ftparchive.h"
b2e465d6 41#include "multicompress.h"
ea542140
DK
42#include "writer.h"
43
44#include <apti18n.h>
b2e465d6
AL
45 /*}}}*/
46
d9e518c6 47using namespace std;
b2e465d6
AL
48unsigned Quiet = 0;
49
50// struct PackageMap - List of all package files in the config file /*{{{*/
51// ---------------------------------------------------------------------
52/* */
53struct PackageMap
54{
55 // General Stuff
56 string BaseDir;
57 string InternalPrefix;
58 string FLFile;
59 string PkgExt;
60 string SrcExt;
61
62 // Stuff for the Package File
63 string PkgFile;
64 string BinCacheDB;
ce928105 65 string SrcCacheDB;
b2e465d6 66 string BinOverride;
64177f17 67 string ExtraOverride;
0b41e0e7
MV
68
69 // We generate for this given arch
70 string Arch;
b2e465d6
AL
71
72 // Stuff for the Source File
73 string SrcFile;
74 string SrcOverride;
64177f17 75 string SrcExtraOverride;
b2e465d6 76
66905344 77 // Translation master file
4e794c50 78 bool LongDesc;
66905344
DK
79 TranslationWriter *TransWriter;
80
b2e465d6
AL
81 // Contents
82 string Contents;
83 string ContentsHead;
84
85 // Random things
86 string Tag;
87 string PkgCompress;
88 string CntCompress;
89 string SrcCompress;
90 string PathPrefix;
91 unsigned int DeLinkLimit;
92 mode_t Permissions;
93
94 bool ContentsDone;
95 bool PkgDone;
96 bool SrcDone;
97 time_t ContentsMTime;
98
99 struct ContentsCompare : public binary_function<PackageMap,PackageMap,bool>
100 {
101 inline bool operator() (const PackageMap &x,const PackageMap &y)
102 {return x.ContentsMTime < y.ContentsMTime;};
103 };
104
105 struct DBCompare : public binary_function<PackageMap,PackageMap,bool>
106 {
107 inline bool operator() (const PackageMap &x,const PackageMap &y)
108 {return x.BinCacheDB < y.BinCacheDB;};
109 };
ce928105
MV
110
111 struct SrcDBCompare : public binary_function<PackageMap,PackageMap,bool>
112 {
113 inline bool operator() (const PackageMap &x,const PackageMap &y)
114 {return x.SrcCacheDB < y.SrcCacheDB;};
115 };
b2e465d6
AL
116
117 void GetGeneral(Configuration &Setup,Configuration &Block);
118 bool GenPackages(Configuration &Setup,struct CacheDB::Stats &Stats);
119 bool GenSources(Configuration &Setup,struct CacheDB::Stats &Stats);
120 bool GenContents(Configuration &Setup,
d935bb17
AL
121 vector<PackageMap>::iterator Begin,
122 vector<PackageMap>::iterator End,
b2e465d6
AL
123 unsigned long &Left);
124
4e794c50 125 PackageMap() : LongDesc(true), TransWriter(NULL), DeLinkLimit(0), Permissions(1),
66905344
DK
126 ContentsDone(false), PkgDone(false), SrcDone(false),
127 ContentsMTime(0) {};
b2e465d6
AL
128};
129 /*}}}*/
130
131// PackageMap::GetGeneral - Common per-section definitions /*{{{*/
132// ---------------------------------------------------------------------
133/* */
134void PackageMap::GetGeneral(Configuration &Setup,Configuration &Block)
135{
136 PathPrefix = Block.Find("PathPrefix");
137
138 if (Block.FindB("External-Links",true) == false)
adfa4116 139 DeLinkLimit = Setup.FindI("Default::DeLinkLimit", std::numeric_limits<unsigned int>::max());
b2e465d6
AL
140 else
141 DeLinkLimit = 0;
142
143 PkgCompress = Block.Find("Packages::Compress",
144 Setup.Find("Default::Packages::Compress",". gzip").c_str());
145 CntCompress = Block.Find("Contents::Compress",
146 Setup.Find("Default::Contents::Compress",". gzip").c_str());
147 SrcCompress = Block.Find("Sources::Compress",
148 Setup.Find("Default::Sources::Compress",". gzip").c_str());
149
150 SrcExt = Block.Find("Sources::Extensions",
151 Setup.Find("Default::Sources::Extensions",".dsc").c_str());
152 PkgExt = Block.Find("Packages::Extensions",
153 Setup.Find("Default::Packages::Extensions",".deb").c_str());
154
2db5b414
DK
155 Permissions = Setup.FindI("Default::FileMode",0644);
156
b2e465d6
AL
157 if (FLFile.empty() == false)
158 FLFile = flCombine(Setup.Find("Dir::FileListDir"),FLFile);
159
160 if (Contents == " ")
161 Contents= string();
162}
163 /*}}}*/
164// PackageMap::GenPackages - Actually generate a Package file /*{{{*/
165// ---------------------------------------------------------------------
166/* This generates the Package File described by this object. */
167bool PackageMap::GenPackages(Configuration &Setup,struct CacheDB::Stats &Stats)
168{
169 if (PkgFile.empty() == true)
170 return true;
171
172 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
173 string OverrideDir = Setup.FindDir("Dir::OverrideDir");
174 string CacheDir = Setup.FindDir("Dir::CacheDir");
175
176 struct timeval StartTime;
177 gettimeofday(&StartTime,0);
178
179 PkgDone = true;
180
181 // Create a package writer object.
88593886
DK
182 MultiCompress Comp(flCombine(ArchiveDir,PkgFile),
183 PkgCompress,Permissions);
3d8232bf 184 PackagesWriter Packages(&Comp.Input, TransWriter, flCombine(CacheDir,BinCacheDB),
64177f17 185 flCombine(OverrideDir,BinOverride),
0b41e0e7
MV
186 flCombine(OverrideDir,ExtraOverride),
187 Arch);
b2e465d6 188 if (PkgExt.empty() == false && Packages.SetExts(PkgExt) == false)
dc738e7a 189 return _error->Error(_("Package extension list is too long"));
b2e465d6 190 if (_error->PendingError() == true)
db0db9fe 191 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
b2e465d6
AL
192
193 Packages.PathPrefix = PathPrefix;
194 Packages.DirStrip = ArchiveDir;
195 Packages.InternalPrefix = flCombine(ArchiveDir,InternalPrefix);
196
4e794c50 197 Packages.LongDescription = LongDesc;
66905344 198
b2e465d6
AL
199 Packages.Stats.DeLinkBytes = Stats.DeLinkBytes;
200 Packages.DeLinkLimit = DeLinkLimit;
64177f17 201
b2e465d6 202 if (_error->PendingError() == true)
db0db9fe 203 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
b2e465d6
AL
204
205 c0out << ' ' << BaseDir << ":" << flush;
206
207 // Do recursive directory searching
208 if (FLFile.empty() == true)
209 {
210 if (Packages.RecursiveScan(flCombine(ArchiveDir,BaseDir)) == false)
211 return false;
212 }
213 else
214 {
215 if (Packages.LoadFileList(ArchiveDir,FLFile) == false)
216 return false;
217 }
218
219 Packages.Output = 0; // Just in case
220
221 // Finish compressing
650faab0 222 unsigned long long Size;
b2e465d6
AL
223 if (Comp.Finalize(Size) == false)
224 {
225 c0out << endl;
db0db9fe 226 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
b2e465d6
AL
227 }
228
229 if (Size != 0)
230 c0out << " New "
231 << SizeToStr(Size) << "B ";
232 else
233 c0out << ' ';
234
235 struct timeval NewTime;
236 gettimeofday(&NewTime,0);
237 double Delta = NewTime.tv_sec - StartTime.tv_sec +
238 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
ce928105 239
b2e465d6
AL
240 c0out << Packages.Stats.Packages << " files " <<
241/* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
242 SizeToStr(Packages.Stats.Bytes) << "B " <<
243 TimeToStr((long)Delta) << endl;
ce928105
MV
244
245 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
246 c0out << " Misses in Cache: " << Packages.Stats.Misses<< endl;
b2e465d6
AL
247
248 Stats.Add(Packages.Stats);
249 Stats.DeLinkBytes = Packages.Stats.DeLinkBytes;
250
251 return !_error->PendingError();
252}
98953965 253
b2e465d6 254 /*}}}*/
64177f17 255// PackageMap::GenSources - Actually generate a Source file /*{{{*/
b2e465d6
AL
256// ---------------------------------------------------------------------
257/* This generates the Sources File described by this object. */
258bool PackageMap::GenSources(Configuration &Setup,struct CacheDB::Stats &Stats)
259{
260 if (SrcFile.empty() == true)
261 return true;
262
263 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
264 string OverrideDir = Setup.FindDir("Dir::OverrideDir");
265 string CacheDir = Setup.FindDir("Dir::CacheDir");
266
267 struct timeval StartTime;
268 gettimeofday(&StartTime,0);
269
270 SrcDone = true;
271
272 // Create a package writer object.
88593886
DK
273 MultiCompress Comp(flCombine(ArchiveDir,SrcFile),
274 SrcCompress,Permissions);
275 SourcesWriter Sources(&Comp.Input, flCombine(CacheDir, SrcCacheDB),
f6f06a8f 276 flCombine(OverrideDir,BinOverride),
64177f17
AL
277 flCombine(OverrideDir,SrcOverride),
278 flCombine(OverrideDir,SrcExtraOverride));
b2e465d6 279 if (SrcExt.empty() == false && Sources.SetExts(SrcExt) == false)
dc738e7a 280 return _error->Error(_("Source extension list is too long"));
b2e465d6 281 if (_error->PendingError() == true)
db0db9fe 282 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
b2e465d6
AL
283
284 Sources.PathPrefix = PathPrefix;
285 Sources.DirStrip = ArchiveDir;
286 Sources.InternalPrefix = flCombine(ArchiveDir,InternalPrefix);
287
288 Sources.DeLinkLimit = DeLinkLimit;
289 Sources.Stats.DeLinkBytes = Stats.DeLinkBytes;
88593886 290
b2e465d6 291 if (_error->PendingError() == true)
db0db9fe 292 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
b2e465d6
AL
293
294 c0out << ' ' << BaseDir << ":" << flush;
295
296 // Do recursive directory searching
297 if (FLFile.empty() == true)
298 {
299 if (Sources.RecursiveScan(flCombine(ArchiveDir,BaseDir))== false)
300 return false;
301 }
302 else
303 {
304 if (Sources.LoadFileList(ArchiveDir,FLFile) == false)
305 return false;
306 }
307 Sources.Output = 0; // Just in case
308
309 // Finish compressing
650faab0 310 unsigned long long Size;
b2e465d6
AL
311 if (Comp.Finalize(Size) == false)
312 {
313 c0out << endl;
db0db9fe 314 return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
b2e465d6
AL
315 }
316
317 if (Size != 0)
318 c0out << " New "
319 << SizeToStr(Size) << "B ";
320 else
321 c0out << ' ';
322
323 struct timeval NewTime;
324 gettimeofday(&NewTime,0);
325 double Delta = NewTime.tv_sec - StartTime.tv_sec +
326 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
327
328 c0out << Sources.Stats.Packages << " pkgs in " <<
329 TimeToStr((long)Delta) << endl;
330
ce928105
MV
331 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
332 c0out << " Misses in Cache: " << Sources.Stats.Misses << endl;
333
b2e465d6
AL
334 Stats.Add(Sources.Stats);
335 Stats.DeLinkBytes = Sources.Stats.DeLinkBytes;
336
337 return !_error->PendingError();
338}
339 /*}}}*/
340// PackageMap::GenContents - Actually generate a Contents file /*{{{*/
341// ---------------------------------------------------------------------
342/* This generates the contents file partially described by this object.
343 It searches the given iterator range for other package files that map
344 into this contents file and includes their data as well when building. */
345bool PackageMap::GenContents(Configuration &Setup,
d935bb17
AL
346 vector<PackageMap>::iterator Begin,
347 vector<PackageMap>::iterator End,
348 unsigned long &Left)
b2e465d6
AL
349{
350 if (Contents.empty() == true)
351 return true;
352
353 if (Left == 0)
354 return true;
355
356 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
357 string CacheDir = Setup.FindDir("Dir::CacheDir");
358 string OverrideDir = Setup.FindDir("Dir::OverrideDir");
359
360 struct timeval StartTime;
361 gettimeofday(&StartTime,0);
362
363 // Create a package writer object.
88593886
DK
364 MultiCompress Comp(flCombine(ArchiveDir,this->Contents),
365 CntCompress,Permissions);
366 Comp.UpdateMTime = Setup.FindI("Default::ContentsAge",10)*24*60*60;
367 ContentsWriter Contents(&Comp.Input, "", Arch);
b2e465d6 368 if (PkgExt.empty() == false && Contents.SetExts(PkgExt) == false)
dc738e7a 369 return _error->Error(_("Package extension list is too long"));
b2e465d6
AL
370 if (_error->PendingError() == true)
371 return false;
372
b2e465d6
AL
373 if (_error->PendingError() == true)
374 return false;
375
376 // Write the header out.
377 if (ContentsHead.empty() == false)
378 {
379 FileFd Head(flCombine(OverrideDir,ContentsHead),FileFd::ReadOnly);
380 if (_error->PendingError() == true)
381 return false;
88593886 382
650faab0 383 unsigned long long Size = Head.Size();
b2e465d6
AL
384 unsigned char Buf[4096];
385 while (Size != 0)
386 {
650faab0 387 unsigned long long ToRead = Size;
b2e465d6
AL
388 if (Size > sizeof(Buf))
389 ToRead = sizeof(Buf);
88593886 390
b2e465d6
AL
391 if (Head.Read(Buf,ToRead) == false)
392 return false;
88593886
DK
393
394 if (Comp.Input.Write(Buf, ToRead) == false)
dc738e7a 395 return _error->Errno("fwrite",_("Error writing header to contents file"));
88593886 396
b2e465d6 397 Size -= ToRead;
88593886
DK
398 }
399 }
400
b2e465d6
AL
401 /* Go over all the package file records and parse all the package
402 files associated with this contents file into one great big honking
403 memory structure, then dump the sorted version */
404 c0out << ' ' << this->Contents << ":" << flush;
f7f0d6c7 405 for (vector<PackageMap>::iterator I = Begin; I != End; ++I)
b2e465d6
AL
406 {
407 if (I->Contents != this->Contents)
408 continue;
409
410 Contents.Prefix = ArchiveDir;
411 Contents.ReadyDB(flCombine(CacheDir,I->BinCacheDB));
412 Contents.ReadFromPkgs(flCombine(ArchiveDir,I->PkgFile),
413 I->PkgCompress);
414
415 I->ContentsDone = true;
416 }
417
418 Contents.Finish();
419
420 // Finish compressing
650faab0 421 unsigned long long Size;
b2e465d6
AL
422 if (Comp.Finalize(Size) == false || _error->PendingError() == true)
423 {
424 c0out << endl;
db0db9fe 425 return _error->Error(_("Error processing contents %s"),
b2e465d6
AL
426 this->Contents.c_str());
427 }
428
429 if (Size != 0)
430 {
431 c0out << " New " << SizeToStr(Size) << "B ";
432 if (Left > Size)
433 Left -= Size;
434 else
435 Left = 0;
436 }
437 else
438 c0out << ' ';
439
440 struct timeval NewTime;
441 gettimeofday(&NewTime,0);
442 double Delta = NewTime.tv_sec - StartTime.tv_sec +
443 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
444
ce928105
MV
445 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
446 c0out << " Misses in Cache: " << Contents.Stats.Misses<< endl;
447
b2e465d6
AL
448 c0out << Contents.Stats.Packages << " files " <<
449 SizeToStr(Contents.Stats.Bytes) << "B " <<
450 TimeToStr((long)Delta) << endl;
451
452 return true;
453}
454 /*}}}*/
455
456// LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
457// ---------------------------------------------------------------------
458/* This populates the PkgList with all the possible permutations of the
459 section/arch lists. */
3d8232bf 460static void LoadTree(vector<PackageMap> &PkgList, std::vector<TranslationWriter*> &TransList, Configuration &Setup)
b2e465d6
AL
461{
462 // Load the defaults
463 string DDir = Setup.Find("TreeDefault::Directory",
464 "$(DIST)/$(SECTION)/binary-$(ARCH)/");
465 string DSDir = Setup.Find("TreeDefault::SrcDirectory",
466 "$(DIST)/$(SECTION)/source/");
467 string DPkg = Setup.Find("TreeDefault::Packages",
468 "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
66905344
DK
469 string DTrans = Setup.Find("TreeDefault::Translation",
470 "$(DIST)/$(SECTION)/i18n/Translation-en");
b2e465d6
AL
471 string DIPrfx = Setup.Find("TreeDefault::InternalPrefix",
472 "$(DIST)/$(SECTION)/");
473 string DContents = Setup.Find("TreeDefault::Contents",
3adddfa8 474 "$(DIST)/$(SECTION)/Contents-$(ARCH)");
b2e465d6
AL
475 string DContentsH = Setup.Find("TreeDefault::Contents::Header","");
476 string DBCache = Setup.Find("TreeDefault::BinCacheDB",
477 "packages-$(ARCH).db");
ce928105
MV
478 string SrcDBCache = Setup.Find("TreeDefault::SrcCacheDB",
479 "sources-$(SECTION).db");
b2e465d6
AL
480 string DSources = Setup.Find("TreeDefault::Sources",
481 "$(DIST)/$(SECTION)/source/Sources");
482 string DFLFile = Setup.Find("TreeDefault::FileList", "");
483 string DSFLFile = Setup.Find("TreeDefault::SourceFileList", "");
484
2db5b414 485 mode_t const Permissions = Setup.FindI("Default::FileMode",0644);
34f1d96c
DK
486
487 bool const LongDescription = Setup.FindB("Default::LongDescription",
4e794c50 488 _config->FindB("APT::FTPArchive::LongDescription", true));
34f1d96c 489 string const TranslationCompress = Setup.Find("Default::Translation::Compress",". gzip").c_str();
4e794c50 490
b2e465d6
AL
491 // Process 'tree' type sections
492 const Configuration::Item *Top = Setup.Tree("tree");
493 for (Top = (Top == 0?0:Top->Child); Top != 0;)
494 {
495 Configuration Block(Top);
496 string Dist = Top->Tag;
497
498 // Parse the sections
d935bb17
AL
499 string Tmp = Block.Find("Sections");
500 const char *Sections = Tmp.c_str();
b2e465d6
AL
501 string Section;
502 while (ParseQuoteWord(Sections,Section) == true)
503 {
b2e465d6 504 string Arch;
66905344
DK
505 struct SubstVar const Vars[] = {{"$(DIST)",&Dist},
506 {"$(SECTION)",&Section},
507 {"$(ARCH)",&Arch},
67c3067f 508 {NULL, NULL}};
34f1d96c 509 mode_t const Perms = Block.FindI("FileMode", Permissions);
4e794c50 510 bool const LongDesc = Block.FindB("LongDescription", LongDescription);
3d8232bf 511 TranslationWriter *TransWriter = NULL;
66905344
DK
512
513 string const Tmp2 = Block.Find("Architectures");
d935bb17 514 const char *Archs = Tmp2.c_str();
b2e465d6
AL
515 while (ParseQuoteWord(Archs,Arch) == true)
516 {
b2e465d6 517 PackageMap Itm;
34f1d96c 518 Itm.Permissions = Perms;
b2e465d6
AL
519 Itm.BinOverride = SubstVar(Block.Find("BinOverride"),Vars);
520 Itm.InternalPrefix = SubstVar(Block.Find("InternalPrefix",DIPrfx.c_str()),Vars);
521
522 if (stringcasecmp(Arch,"source") == 0)
523 {
524 Itm.SrcOverride = SubstVar(Block.Find("SrcOverride"),Vars);
525 Itm.BaseDir = SubstVar(Block.Find("SrcDirectory",DSDir.c_str()),Vars);
526 Itm.SrcFile = SubstVar(Block.Find("Sources",DSources.c_str()),Vars);
527 Itm.Tag = SubstVar("$(DIST)/$(SECTION)/source",Vars);
528 Itm.FLFile = SubstVar(Block.Find("SourceFileList",DSFLFile.c_str()),Vars);
64177f17 529 Itm.SrcExtraOverride = SubstVar(Block.Find("SrcExtraOverride"),Vars);
ce928105 530 Itm.SrcCacheDB = SubstVar(Block.Find("SrcCacheDB",SrcDBCache.c_str()),Vars);
b2e465d6
AL
531 }
532 else
533 {
534 Itm.BinCacheDB = SubstVar(Block.Find("BinCacheDB",DBCache.c_str()),Vars);
535 Itm.BaseDir = SubstVar(Block.Find("Directory",DDir.c_str()),Vars);
536 Itm.PkgFile = SubstVar(Block.Find("Packages",DPkg.c_str()),Vars);
537 Itm.Tag = SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars);
0b41e0e7 538 Itm.Arch = Arch;
4e794c50 539 Itm.LongDesc = LongDesc;
3d8232bf 540 if (TransWriter == NULL && DTrans.empty() == false && LongDesc == false && DTrans != "/dev/null")
66905344 541 {
3d8232bf
DK
542 string const TranslationFile = flCombine(Setup.FindDir("Dir::ArchiveDir"),
543 SubstVar(Block.Find("Translation", DTrans.c_str()), Vars));
544 string const TransCompress = Block.Find("Translation::Compress", TranslationCompress);
545 TransWriter = new TranslationWriter(TranslationFile, TransCompress, Perms);
546 TransList.push_back(TransWriter);
66905344 547 }
3d8232bf 548 Itm.TransWriter = TransWriter;
b2e465d6
AL
549 Itm.Contents = SubstVar(Block.Find("Contents",DContents.c_str()),Vars);
550 Itm.ContentsHead = SubstVar(Block.Find("Contents::Header",DContentsH.c_str()),Vars);
551 Itm.FLFile = SubstVar(Block.Find("FileList",DFLFile.c_str()),Vars);
64177f17 552 Itm.ExtraOverride = SubstVar(Block.Find("ExtraOverride"),Vars);
b2e465d6
AL
553 }
554
3d8232bf 555 Itm.GetGeneral(Setup,Block);
b2e465d6
AL
556 PkgList.push_back(Itm);
557 }
558 }
3d8232bf 559
b2e465d6 560 Top = Top->Next;
3d8232bf
DK
561 }
562}
563 /*}}}*/
564static void UnloadTree(std::vector<TranslationWriter*> const &Trans) /*{{{*/
565{
566 for (std::vector<TranslationWriter*>::const_reverse_iterator T = Trans.rbegin(); T != Trans.rend(); ++T)
567 delete *T;
b2e465d6
AL
568}
569 /*}}}*/
570// LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
571// ---------------------------------------------------------------------
572/* */
c3ccac92 573static void LoadBinDir(vector<PackageMap> &PkgList,Configuration &Setup)
b2e465d6 574{
2db5b414
DK
575 mode_t const Permissions = Setup.FindI("Default::FileMode",0644);
576
b2e465d6
AL
577 // Process 'bindirectory' type sections
578 const Configuration::Item *Top = Setup.Tree("bindirectory");
579 for (Top = (Top == 0?0:Top->Child); Top != 0;)
580 {
581 Configuration Block(Top);
582
583 PackageMap Itm;
584 Itm.PkgFile = Block.Find("Packages");
585 Itm.SrcFile = Block.Find("Sources");
586 Itm.BinCacheDB = Block.Find("BinCacheDB");
ce928105 587 Itm.SrcCacheDB = Block.Find("SrcCacheDB");
b2e465d6 588 Itm.BinOverride = Block.Find("BinOverride");
64177f17
AL
589 Itm.ExtraOverride = Block.Find("ExtraOverride");
590 Itm.SrcExtraOverride = Block.Find("SrcExtraOverride");
b2e465d6
AL
591 Itm.SrcOverride = Block.Find("SrcOverride");
592 Itm.BaseDir = Top->Tag;
593 Itm.FLFile = Block.Find("FileList");
594 Itm.InternalPrefix = Block.Find("InternalPrefix",Top->Tag.c_str());
595 Itm.Contents = Block.Find("Contents");
596 Itm.ContentsHead = Block.Find("Contents::Header");
2db5b414 597 Itm.Permissions = Block.FindI("FileMode", Permissions);
b2e465d6
AL
598
599 Itm.GetGeneral(Setup,Block);
600 PkgList.push_back(Itm);
601
602 Top = Top->Next;
603 }
604}
605 /*}}}*/
606
607// ShowHelp - Show the help text /*{{{*/
608// ---------------------------------------------------------------------
609/* */
cbbee23e 610static bool ShowHelp(CommandLine &, CommandLine::DispatchWithHelp const *)
b2e465d6 611{
249aec3b 612 ioprintf(cout, "%s %s (%s)\n", PACKAGE, PACKAGE_VERSION, COMMON_ARCH);
b2e465d6
AL
613 if (_config->FindB("version") == true)
614 return true;
615
616 cout <<
dc738e7a 617 _("Usage: apt-ftparchive [options] command\n"
7a3e9e0a 618 "Commands: packages binarypath [overridefile [pathprefix]]\n"
b2e465d6
AL
619 " sources srcpath [overridefile [pathprefix]]\n"
620 " contents path\n"
0dfd2728 621 " release path\n"
b2e465d6
AL
622 " generate config [groups]\n"
623 " clean config\n"
624 "\n"
625 "apt-ftparchive generates index files for Debian archives. It supports\n"
626 "many styles of generation from fully automated to functional replacements\n"
627 "for dpkg-scanpackages and dpkg-scansources\n"
628 "\n"
629 "apt-ftparchive generates Package files from a tree of .debs. The\n"
630 "Package file contains the contents of all the control fields from\n"
631 "each package as well as the MD5 hash and filesize. An override file\n"
632 "is supported to force the value of Priority and Section.\n"
633 "\n"
634 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
635 "The --source-override option can be used to specify a src override file\n"
636 "\n"
637 "The 'packages' and 'sources' command should be run in the root of the\n"
638 "tree. BinaryPath should point to the base of the recursive search and \n"
75224826 639 "override file should contain the override flags. Pathprefix is\n"
b2e465d6 640 "appended to the filename fields if present. Example usage from the \n"
75224826 641 "Debian archive:\n"
b2e465d6
AL
642 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
643 " dists/potato/main/binary-i386/Packages\n"
644 "\n"
645 "Options:\n"
646 " -h This help text\n"
647 " --md5 Control MD5 generation\n"
648 " -s=? Source override file\n"
649 " -q Quiet\n"
650 " -d=? Select the optional caching database\n"
651 " --no-delink Enable delinking debug mode\n"
652 " --contents Control contents file generation\n"
653 " -c=? Read this configuration file\n"
8a2d7db6 654 " -o=? Set an arbitrary configuration option") << endl;
b2e465d6
AL
655
656 return true;
657}
658 /*}}}*/
659// SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
660// ---------------------------------------------------------------------
661/* This emulates dpkg-scanpackages's command line interface. 'mostly' */
c3ccac92 662static bool SimpleGenPackages(CommandLine &CmdL)
b2e465d6
AL
663{
664 if (CmdL.FileSize() < 2)
cbbee23e 665 return ShowHelp(CmdL, nullptr);
b2e465d6
AL
666
667 string Override;
668 if (CmdL.FileSize() >= 3)
669 Override = CmdL.FileList[2];
670
671 // Create a package writer object.
3d8232bf 672 PackagesWriter Packages(NULL, NULL, _config->Find("APT::FTPArchive::DB"),
31981076 673 Override, "", _config->Find("APT::FTPArchive::Architecture"));
b2e465d6
AL
674 if (_error->PendingError() == true)
675 return false;
676
677 if (CmdL.FileSize() >= 4)
678 Packages.PathPrefix = CmdL.FileList[3];
679
680 // Do recursive directory searching
681 if (Packages.RecursiveScan(CmdL.FileList[1]) == false)
682 return false;
683
cf6bbca0
MV
684 // Give some stats if asked for
685 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
686 c0out << " Misses in Cache: " << Packages.Stats.Misses<< endl;
687
b2e465d6
AL
688 return true;
689}
690 /*}}}*/
691// SimpleGenContents - Generate a Contents listing /*{{{*/
692// ---------------------------------------------------------------------
693/* */
c3ccac92 694static bool SimpleGenContents(CommandLine &CmdL)
b2e465d6
AL
695{
696 if (CmdL.FileSize() < 2)
cbbee23e 697 return ShowHelp(CmdL, nullptr);
b2e465d6
AL
698
699 // Create a package writer object.
88593886 700 ContentsWriter Contents(NULL, _config->Find("APT::FTPArchive::DB"), _config->Find("APT::FTPArchive::Architecture"));
b2e465d6
AL
701 if (_error->PendingError() == true)
702 return false;
703
704 // Do recursive directory searching
705 if (Contents.RecursiveScan(CmdL.FileList[1]) == false)
706 return false;
707
708 Contents.Finish();
709
710 return true;
711}
712 /*}}}*/
713// SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
714// ---------------------------------------------------------------------
715/* This emulates dpkg-scanpackages's command line interface. 'mostly' */
c3ccac92 716static bool SimpleGenSources(CommandLine &CmdL)
b2e465d6
AL
717{
718 if (CmdL.FileSize() < 2)
cbbee23e 719 return ShowHelp(CmdL, nullptr);
b2e465d6
AL
720
721 string Override;
722 if (CmdL.FileSize() >= 3)
723 Override = CmdL.FileList[2];
724
725 string SOverride;
726 if (Override.empty() == false)
727 SOverride = Override + ".src";
728
729 SOverride = _config->Find("APT::FTPArchive::SourceOverride",
730 SOverride.c_str());
731
732 // Create a package writer object.
88593886 733 SourcesWriter Sources(NULL, _config->Find("APT::FTPArchive::DB"),Override,SOverride);
b2e465d6
AL
734 if (_error->PendingError() == true)
735 return false;
736
737 if (CmdL.FileSize() >= 4)
738 Sources.PathPrefix = CmdL.FileList[3];
739
740 // Do recursive directory searching
741 if (Sources.RecursiveScan(CmdL.FileList[1]) == false)
742 return false;
743
cf6bbca0
MV
744 // Give some stats if asked for
745 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
746 c0out << " Misses in Cache: " << Sources.Stats.Misses<< endl;
747
b2e465d6
AL
748 return true;
749}
98953965
AL
750 /*}}}*/
751// SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
752// ---------------------------------------------------------------------
c3ccac92 753static bool SimpleGenRelease(CommandLine &CmdL)
98953965 754{
0dfd2728 755 if (CmdL.FileSize() < 2)
cbbee23e 756 return ShowHelp(CmdL, nullptr);
0dfd2728 757
c0eb6bc6
AL
758 string Dir = CmdL.FileList[1];
759
88593886 760 ReleaseWriter Release(NULL, "");
c0eb6bc6
AL
761 Release.DirStrip = Dir;
762
98953965
AL
763 if (_error->PendingError() == true)
764 return false;
765
c0eb6bc6 766 if (Release.RecursiveScan(Dir) == false)
98953965
AL
767 return false;
768
f7291f62
AL
769 Release.Finish();
770
98953965
AL
771 return true;
772}
773
b2e465d6 774 /*}}}*/
308b6fc9 775// DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
b2e465d6 776// ---------------------------------------------------------------------
308b6fc9
DJL
777static bool DoGeneratePackagesAndSources(Configuration &Setup,
778 vector<PackageMap> &PkgList,
779 struct CacheDB::Stats &SrcStats,
780 struct CacheDB::Stats &Stats,
781 CommandLine &CmdL)
b2e465d6 782{
b2e465d6
AL
783 if (CmdL.FileSize() <= 2)
784 {
f7f0d6c7 785 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6
AL
786 if (I->GenPackages(Setup,Stats) == false)
787 _error->DumpErrors();
f7f0d6c7 788 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6
AL
789 if (I->GenSources(Setup,SrcStats) == false)
790 _error->DumpErrors();
791 }
792 else
793 {
794 // Make a choice list out of the package list..
795 RxChoiceList *List = new RxChoiceList[2*PkgList.size()+1];
796 RxChoiceList *End = List;
f7f0d6c7 797 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6 798 {
d935bb17 799 End->UserData = &(*I);
b2e465d6
AL
800 End->Str = I->BaseDir.c_str();
801 End++;
802
d935bb17 803 End->UserData = &(*I);
b2e465d6
AL
804 End->Str = I->Tag.c_str();
805 End++;
806 }
807 End->Str = 0;
808
809 // Regex it
810 if (RegexChoice(List,CmdL.FileList + 2,CmdL.FileList + CmdL.FileSize()) == 0)
811 {
812 delete [] List;
dc738e7a 813 return _error->Error(_("No selections matched"));
b2e465d6
AL
814 }
815 _error->DumpErrors();
816
817 // Do the generation for Packages
818 for (End = List; End->Str != 0; End++)
819 {
820 if (End->Hit == false)
821 continue;
822
823 PackageMap *I = (PackageMap *)End->UserData;
824 if (I->PkgDone == true)
825 continue;
826 if (I->GenPackages(Setup,Stats) == false)
827 _error->DumpErrors();
828 }
829
830 // Do the generation for Sources
831 for (End = List; End->Str != 0; End++)
832 {
833 if (End->Hit == false)
834 continue;
835
836 PackageMap *I = (PackageMap *)End->UserData;
837 if (I->SrcDone == true)
838 continue;
839 if (I->GenSources(Setup,SrcStats) == false)
840 _error->DumpErrors();
841 }
842
843 delete [] List;
844 }
308b6fc9
DJL
845 return true;
846}
847
848 /*}}}*/
849// DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
850// ---------------------------------------------------------------------
851static bool DoGenerateContents(Configuration &Setup,
852 vector<PackageMap> &PkgList,
853 CommandLine &CmdL)
854{
c6474fb6 855 c1out << "Packages done, Starting contents." << endl;
b2e465d6
AL
856
857 // Sort the contents file list by date
858 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
f7f0d6c7 859 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6
AL
860 {
861 struct stat A;
862 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),
863 I->CntCompress,A) == false)
864 time(&I->ContentsMTime);
865 else
866 I->ContentsMTime = A.st_mtime;
867 }
868 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::ContentsCompare());
869
870 /* Now for Contents.. The process here is to do a make-like dependency
871 check. Each contents file is verified to be newer than the package files
872 that describe the debs it indexes. Since the package files contain
873 hashes of the .debs this means they have not changed either so the
874 contents must be up to date. */
adfa4116
JAK
875 unsigned long MaxContentsChange = Setup.FindI("Default::MaxContentsChange",
876 std::numeric_limits<unsigned int>::max())*1024;
f7f0d6c7 877 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6 878 {
1e3f4083 879 // This record is not relevant
b2e465d6
AL
880 if (I->ContentsDone == true ||
881 I->Contents.empty() == true)
882 continue;
883
884 // Do not do everything if the user specified sections.
885 if (CmdL.FileSize() > 2 && I->PkgDone == false)
886 continue;
887
888 struct stat A,B;
889 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),I->CntCompress,A) == true)
890 {
891 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->PkgFile),I->PkgCompress,B) == false)
892 {
dc738e7a 893 _error->Warning(_("Some files are missing in the package file group `%s'"),I->PkgFile.c_str());
b2e465d6
AL
894 continue;
895 }
896
897 if (A.st_mtime > B.st_mtime)
898 continue;
899 }
900
901 if (I->GenContents(Setup,PkgList.begin(),PkgList.end(),
902 MaxContentsChange) == false)
903 _error->DumpErrors();
904
905 // Hit the limit?
906 if (MaxContentsChange == 0)
907 {
908 c1out << "Hit contents update byte limit" << endl;
909 break;
910 }
911 }
308b6fc9
DJL
912
913 return true;
914}
915
916 /*}}}*/
917// Generate - Full generate, using a config file /*{{{*/
918// ---------------------------------------------------------------------
919/* */
920static bool Generate(CommandLine &CmdL)
921{
922 struct CacheDB::Stats SrcStats;
923 if (CmdL.FileSize() < 2)
cbbee23e 924 return ShowHelp(CmdL, nullptr);
308b6fc9
DJL
925
926 struct timeval StartTime;
927 gettimeofday(&StartTime,0);
928 struct CacheDB::Stats Stats;
b2e465d6 929
308b6fc9
DJL
930 // Read the configuration file.
931 Configuration Setup;
932 if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
933 return false;
934
935 vector<PackageMap> PkgList;
3d8232bf
DK
936 std::vector<TranslationWriter*> TransList;
937 LoadTree(PkgList, TransList, Setup);
308b6fc9
DJL
938 LoadBinDir(PkgList,Setup);
939
940 // Sort by cache DB to improve IO locality.
941 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
942 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::SrcDBCompare());
943
944 // Generate packages
945 if (_config->FindB("APT::FTPArchive::ContentsOnly", false) == false)
946 {
947 if(DoGeneratePackagesAndSources(Setup, PkgList, SrcStats, Stats, CmdL) == false)
3d8232bf
DK
948 {
949 UnloadTree(TransList);
308b6fc9 950 return false;
3d8232bf 951 }
308b6fc9
DJL
952 } else {
953 c1out << "Skipping Packages/Sources generation" << endl;
954 }
955
956 // do Contents if needed
957 if (_config->FindB("APT::FTPArchive::Contents", true) == true)
958 if (DoGenerateContents(Setup, PkgList, CmdL) == false)
3d8232bf
DK
959 {
960 UnloadTree(TransList);
961 return false;
962 }
308b6fc9 963
b2e465d6 964 struct timeval NewTime;
308b6fc9
DJL
965 gettimeofday(&NewTime,0);
966 double Delta = NewTime.tv_sec - StartTime.tv_sec +
b2e465d6 967 (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
308b6fc9 968 c1out << "Done. " << SizeToStr(Stats.Bytes) << "B in " << Stats.Packages
b2e465d6 969 << " archives. Took " << TimeToStr((long)Delta) << endl;
308b6fc9 970
3d8232bf 971 UnloadTree(TransList);
b2e465d6
AL
972 return true;
973}
308b6fc9
DJL
974
975 /*}}}*/
b2e465d6
AL
976// Clean - Clean out the databases /*{{{*/
977// ---------------------------------------------------------------------
978/* */
c3ccac92 979static bool Clean(CommandLine &CmdL)
b2e465d6
AL
980{
981 if (CmdL.FileSize() != 2)
cbbee23e 982 return ShowHelp(CmdL, nullptr);
b2e465d6
AL
983
984 // Read the configuration file.
985 Configuration Setup;
986 if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
987 return false;
3d8232bf
DK
988 // we don't need translation creation here
989 Setup.Set("TreeDefault::Translation", "/dev/null");
b2e465d6
AL
990
991 vector<PackageMap> PkgList;
3d8232bf
DK
992 std::vector<TranslationWriter*> TransList;
993 LoadTree(PkgList, TransList, Setup);
b2e465d6
AL
994 LoadBinDir(PkgList,Setup);
995
996 // Sort by cache DB to improve IO locality.
997 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
53ba4e2c 998 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::SrcDBCompare());
b2e465d6
AL
999
1000 string CacheDir = Setup.FindDir("Dir::CacheDir");
1001
d935bb17 1002 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); )
b2e465d6 1003 {
53ba4e2c
MV
1004 if(I->BinCacheDB != "")
1005 c0out << I->BinCacheDB << endl;
1006 if(I->SrcCacheDB != "")
1007 c0out << I->SrcCacheDB << endl;
b2e465d6 1008 CacheDB DB(flCombine(CacheDir,I->BinCacheDB));
53ba4e2c 1009 CacheDB DB_SRC(flCombine(CacheDir,I->SrcCacheDB));
b2e465d6
AL
1010 if (DB.Clean() == false)
1011 _error->DumpErrors();
53ba4e2c
MV
1012 if (DB_SRC.Clean() == false)
1013 _error->DumpErrors();
3d8232bf 1014
b2e465d6 1015 string CacheDB = I->BinCacheDB;
53ba4e2c
MV
1016 string SrcCacheDB = I->SrcCacheDB;
1017 while(I != PkgList.end() &&
1018 I->BinCacheDB == CacheDB &&
1019 I->SrcCacheDB == SrcCacheDB)
1020 ++I;
b2e465d6 1021 }
ce928105 1022
53ba4e2c 1023
b2e465d6
AL
1024 return true;
1025}
1026 /*}}}*/
1027
1028int main(int argc, const char *argv[])
1029{
e7e10e47 1030 InitLocale();
cbbee23e
DK
1031
1032 CommandLine::DispatchWithHelp Cmds[] = {
1033 {"packages",&SimpleGenPackages, nullptr},
1034 {"contents",&SimpleGenContents, nullptr},
1035 {"sources",&SimpleGenSources, nullptr},
1036 {"release",&SimpleGenRelease, nullptr},
1037 {"generate",&Generate, nullptr},
1038 {"clean",&Clean, nullptr},
1039 {nullptr, nullptr, nullptr}
1040 };
b2e465d6
AL
1041
1042 // Parse the command line and initialize the package library
e7e10e47
DK
1043 CommandLine CmdL;
1044 ParseCommandLine(CmdL, Cmds, "apt-ftparchive", &_config, NULL, argc, argv, ShowHelp);
ad7e0941 1045
d9e518c6 1046 _config->CndSet("quiet",0);
b2e465d6 1047 Quiet = _config->FindI("quiet",0);
d9e518c6
DK
1048 InitOutput(clog.rdbuf());
1049
e7e10e47 1050 return DispatchCommandLine(CmdL, Cmds);
b2e465d6 1051}