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