]> git.saurik.com Git - apt.git/blame - ftparchive/apt-ftparchive.cc
call URIStart in cdrom and file method
[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)
138 DeLinkLimit = Setup.FindI("Default::DeLinkLimit",UINT_MAX);
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);
183 PackagesWriter Packages(&Comp.Input, 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
66905344 196 Packages.TransWriter = TransWriter;
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. */
c3ccac92 460static void LoadTree(vector<PackageMap> &PkgList,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);
66905344 511 TranslationWriter *TransWriter;
4e794c50 512 if (DTrans.empty() == false && LongDesc == false)
66905344
DK
513 {
514 string const TranslationFile = flCombine(Setup.FindDir("Dir::ArchiveDir"),
515 SubstVar(Block.Find("Translation", DTrans.c_str()), Vars));
34f1d96c
DK
516 string const TransCompress = Block.Find("Translation::Compress", TranslationCompress);
517 TransWriter = new TranslationWriter(TranslationFile, TransCompress, Perms);
66905344
DK
518 }
519 else
520 TransWriter = NULL;
521
522 string const Tmp2 = Block.Find("Architectures");
d935bb17 523 const char *Archs = Tmp2.c_str();
b2e465d6
AL
524 while (ParseQuoteWord(Archs,Arch) == true)
525 {
b2e465d6 526 PackageMap Itm;
34f1d96c 527 Itm.Permissions = Perms;
b2e465d6
AL
528 Itm.BinOverride = SubstVar(Block.Find("BinOverride"),Vars);
529 Itm.InternalPrefix = SubstVar(Block.Find("InternalPrefix",DIPrfx.c_str()),Vars);
530
531 if (stringcasecmp(Arch,"source") == 0)
532 {
533 Itm.SrcOverride = SubstVar(Block.Find("SrcOverride"),Vars);
534 Itm.BaseDir = SubstVar(Block.Find("SrcDirectory",DSDir.c_str()),Vars);
535 Itm.SrcFile = SubstVar(Block.Find("Sources",DSources.c_str()),Vars);
536 Itm.Tag = SubstVar("$(DIST)/$(SECTION)/source",Vars);
537 Itm.FLFile = SubstVar(Block.Find("SourceFileList",DSFLFile.c_str()),Vars);
64177f17 538 Itm.SrcExtraOverride = SubstVar(Block.Find("SrcExtraOverride"),Vars);
ce928105 539 Itm.SrcCacheDB = SubstVar(Block.Find("SrcCacheDB",SrcDBCache.c_str()),Vars);
b2e465d6
AL
540 }
541 else
542 {
543 Itm.BinCacheDB = SubstVar(Block.Find("BinCacheDB",DBCache.c_str()),Vars);
544 Itm.BaseDir = SubstVar(Block.Find("Directory",DDir.c_str()),Vars);
545 Itm.PkgFile = SubstVar(Block.Find("Packages",DPkg.c_str()),Vars);
546 Itm.Tag = SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars);
0b41e0e7 547 Itm.Arch = Arch;
4e794c50 548 Itm.LongDesc = LongDesc;
66905344
DK
549 if (TransWriter != NULL)
550 {
551 TransWriter->IncreaseRefCounter();
552 Itm.TransWriter = TransWriter;
553 }
b2e465d6
AL
554 Itm.Contents = SubstVar(Block.Find("Contents",DContents.c_str()),Vars);
555 Itm.ContentsHead = SubstVar(Block.Find("Contents::Header",DContentsH.c_str()),Vars);
556 Itm.FLFile = SubstVar(Block.Find("FileList",DFLFile.c_str()),Vars);
64177f17 557 Itm.ExtraOverride = SubstVar(Block.Find("ExtraOverride"),Vars);
b2e465d6
AL
558 }
559
560 Itm.GetGeneral(Setup,Block);
561 PkgList.push_back(Itm);
562 }
66905344
DK
563 // we didn't use this TransWriter, so we can release it
564 if (TransWriter != NULL && TransWriter->GetRefCounter() == 0)
565 delete TransWriter;
b2e465d6
AL
566 }
567
568 Top = Top->Next;
569 }
570}
571 /*}}}*/
572// LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
573// ---------------------------------------------------------------------
574/* */
c3ccac92 575static void LoadBinDir(vector<PackageMap> &PkgList,Configuration &Setup)
b2e465d6 576{
2db5b414
DK
577 mode_t const Permissions = Setup.FindI("Default::FileMode",0644);
578
b2e465d6
AL
579 // Process 'bindirectory' type sections
580 const Configuration::Item *Top = Setup.Tree("bindirectory");
581 for (Top = (Top == 0?0:Top->Child); Top != 0;)
582 {
583 Configuration Block(Top);
584
585 PackageMap Itm;
586 Itm.PkgFile = Block.Find("Packages");
587 Itm.SrcFile = Block.Find("Sources");
588 Itm.BinCacheDB = Block.Find("BinCacheDB");
ce928105 589 Itm.SrcCacheDB = Block.Find("SrcCacheDB");
b2e465d6 590 Itm.BinOverride = Block.Find("BinOverride");
64177f17
AL
591 Itm.ExtraOverride = Block.Find("ExtraOverride");
592 Itm.SrcExtraOverride = Block.Find("SrcExtraOverride");
b2e465d6
AL
593 Itm.SrcOverride = Block.Find("SrcOverride");
594 Itm.BaseDir = Top->Tag;
595 Itm.FLFile = Block.Find("FileList");
596 Itm.InternalPrefix = Block.Find("InternalPrefix",Top->Tag.c_str());
597 Itm.Contents = Block.Find("Contents");
598 Itm.ContentsHead = Block.Find("Contents::Header");
2db5b414 599 Itm.Permissions = Block.FindI("FileMode", Permissions);
b2e465d6
AL
600
601 Itm.GetGeneral(Setup,Block);
602 PkgList.push_back(Itm);
603
604 Top = Top->Next;
605 }
606}
607 /*}}}*/
608
609// ShowHelp - Show the help text /*{{{*/
610// ---------------------------------------------------------------------
611/* */
65512241 612static bool ShowHelp(CommandLine &)
b2e465d6 613{
249aec3b 614 ioprintf(cout, "%s %s (%s)\n", PACKAGE, PACKAGE_VERSION, COMMON_ARCH);
b2e465d6
AL
615 if (_config->FindB("version") == true)
616 return true;
617
618 cout <<
dc738e7a 619 _("Usage: apt-ftparchive [options] command\n"
7a3e9e0a 620 "Commands: packages binarypath [overridefile [pathprefix]]\n"
b2e465d6
AL
621 " sources srcpath [overridefile [pathprefix]]\n"
622 " contents path\n"
0dfd2728 623 " release path\n"
b2e465d6
AL
624 " generate config [groups]\n"
625 " clean config\n"
626 "\n"
627 "apt-ftparchive generates index files for Debian archives. It supports\n"
628 "many styles of generation from fully automated to functional replacements\n"
629 "for dpkg-scanpackages and dpkg-scansources\n"
630 "\n"
631 "apt-ftparchive generates Package files from a tree of .debs. The\n"
632 "Package file contains the contents of all the control fields from\n"
633 "each package as well as the MD5 hash and filesize. An override file\n"
634 "is supported to force the value of Priority and Section.\n"
635 "\n"
636 "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
637 "The --source-override option can be used to specify a src override file\n"
638 "\n"
639 "The 'packages' and 'sources' command should be run in the root of the\n"
640 "tree. BinaryPath should point to the base of the recursive search and \n"
75224826 641 "override file should contain the override flags. Pathprefix is\n"
b2e465d6 642 "appended to the filename fields if present. Example usage from the \n"
75224826 643 "Debian archive:\n"
b2e465d6
AL
644 " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
645 " dists/potato/main/binary-i386/Packages\n"
646 "\n"
647 "Options:\n"
648 " -h This help text\n"
649 " --md5 Control MD5 generation\n"
650 " -s=? Source override file\n"
651 " -q Quiet\n"
652 " -d=? Select the optional caching database\n"
653 " --no-delink Enable delinking debug mode\n"
654 " --contents Control contents file generation\n"
655 " -c=? Read this configuration file\n"
8a2d7db6 656 " -o=? Set an arbitrary configuration option") << endl;
b2e465d6
AL
657
658 return true;
659}
660 /*}}}*/
661// SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
662// ---------------------------------------------------------------------
663/* This emulates dpkg-scanpackages's command line interface. 'mostly' */
c3ccac92 664static bool SimpleGenPackages(CommandLine &CmdL)
b2e465d6
AL
665{
666 if (CmdL.FileSize() < 2)
667 return ShowHelp(CmdL);
668
669 string Override;
670 if (CmdL.FileSize() >= 3)
671 Override = CmdL.FileList[2];
672
673 // Create a package writer object.
88593886 674 PackagesWriter Packages(NULL, _config->Find("APT::FTPArchive::DB"),
31981076 675 Override, "", _config->Find("APT::FTPArchive::Architecture"));
b2e465d6
AL
676 if (_error->PendingError() == true)
677 return false;
678
679 if (CmdL.FileSize() >= 4)
680 Packages.PathPrefix = CmdL.FileList[3];
681
682 // Do recursive directory searching
683 if (Packages.RecursiveScan(CmdL.FileList[1]) == false)
684 return false;
685
cf6bbca0
MV
686 // Give some stats if asked for
687 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
688 c0out << " Misses in Cache: " << Packages.Stats.Misses<< endl;
689
b2e465d6
AL
690 return true;
691}
692 /*}}}*/
693// SimpleGenContents - Generate a Contents listing /*{{{*/
694// ---------------------------------------------------------------------
695/* */
c3ccac92 696static bool SimpleGenContents(CommandLine &CmdL)
b2e465d6
AL
697{
698 if (CmdL.FileSize() < 2)
699 return ShowHelp(CmdL);
700
701 // Create a package writer object.
88593886 702 ContentsWriter Contents(NULL, _config->Find("APT::FTPArchive::DB"), _config->Find("APT::FTPArchive::Architecture"));
b2e465d6
AL
703 if (_error->PendingError() == true)
704 return false;
705
706 // Do recursive directory searching
707 if (Contents.RecursiveScan(CmdL.FileList[1]) == false)
708 return false;
709
710 Contents.Finish();
711
712 return true;
713}
714 /*}}}*/
715// SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
716// ---------------------------------------------------------------------
717/* This emulates dpkg-scanpackages's command line interface. 'mostly' */
c3ccac92 718static bool SimpleGenSources(CommandLine &CmdL)
b2e465d6
AL
719{
720 if (CmdL.FileSize() < 2)
721 return ShowHelp(CmdL);
722
723 string Override;
724 if (CmdL.FileSize() >= 3)
725 Override = CmdL.FileList[2];
726
727 string SOverride;
728 if (Override.empty() == false)
729 SOverride = Override + ".src";
730
731 SOverride = _config->Find("APT::FTPArchive::SourceOverride",
732 SOverride.c_str());
733
734 // Create a package writer object.
88593886 735 SourcesWriter Sources(NULL, _config->Find("APT::FTPArchive::DB"),Override,SOverride);
b2e465d6
AL
736 if (_error->PendingError() == true)
737 return false;
738
739 if (CmdL.FileSize() >= 4)
740 Sources.PathPrefix = CmdL.FileList[3];
741
742 // Do recursive directory searching
743 if (Sources.RecursiveScan(CmdL.FileList[1]) == false)
744 return false;
745
cf6bbca0
MV
746 // Give some stats if asked for
747 if(_config->FindB("APT::FTPArchive::ShowCacheMisses", false) == true)
748 c0out << " Misses in Cache: " << Sources.Stats.Misses<< endl;
749
b2e465d6
AL
750 return true;
751}
98953965
AL
752 /*}}}*/
753// SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
754// ---------------------------------------------------------------------
c3ccac92 755static bool SimpleGenRelease(CommandLine &CmdL)
98953965 756{
0dfd2728
AL
757 if (CmdL.FileSize() < 2)
758 return ShowHelp(CmdL);
759
c0eb6bc6
AL
760 string Dir = CmdL.FileList[1];
761
88593886 762 ReleaseWriter Release(NULL, "");
c0eb6bc6
AL
763 Release.DirStrip = Dir;
764
98953965
AL
765 if (_error->PendingError() == true)
766 return false;
767
c0eb6bc6 768 if (Release.RecursiveScan(Dir) == false)
98953965
AL
769 return false;
770
f7291f62
AL
771 Release.Finish();
772
98953965
AL
773 return true;
774}
775
b2e465d6 776 /*}}}*/
308b6fc9 777// DoGeneratePackagesAndSources - Helper for Generate /*{{{*/
b2e465d6 778// ---------------------------------------------------------------------
308b6fc9
DJL
779static bool DoGeneratePackagesAndSources(Configuration &Setup,
780 vector<PackageMap> &PkgList,
781 struct CacheDB::Stats &SrcStats,
782 struct CacheDB::Stats &Stats,
783 CommandLine &CmdL)
b2e465d6 784{
b2e465d6
AL
785 if (CmdL.FileSize() <= 2)
786 {
f7f0d6c7 787 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6
AL
788 if (I->GenPackages(Setup,Stats) == false)
789 _error->DumpErrors();
f7f0d6c7 790 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6
AL
791 if (I->GenSources(Setup,SrcStats) == false)
792 _error->DumpErrors();
793 }
794 else
795 {
796 // Make a choice list out of the package list..
797 RxChoiceList *List = new RxChoiceList[2*PkgList.size()+1];
798 RxChoiceList *End = List;
f7f0d6c7 799 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6 800 {
d935bb17 801 End->UserData = &(*I);
b2e465d6
AL
802 End->Str = I->BaseDir.c_str();
803 End++;
804
d935bb17 805 End->UserData = &(*I);
b2e465d6
AL
806 End->Str = I->Tag.c_str();
807 End++;
808 }
809 End->Str = 0;
810
811 // Regex it
812 if (RegexChoice(List,CmdL.FileList + 2,CmdL.FileList + CmdL.FileSize()) == 0)
813 {
814 delete [] List;
dc738e7a 815 return _error->Error(_("No selections matched"));
b2e465d6
AL
816 }
817 _error->DumpErrors();
818
819 // Do the generation for Packages
820 for (End = List; End->Str != 0; End++)
821 {
822 if (End->Hit == false)
823 continue;
824
825 PackageMap *I = (PackageMap *)End->UserData;
826 if (I->PkgDone == true)
827 continue;
828 if (I->GenPackages(Setup,Stats) == false)
829 _error->DumpErrors();
830 }
831
832 // Do the generation for Sources
833 for (End = List; End->Str != 0; End++)
834 {
835 if (End->Hit == false)
836 continue;
837
838 PackageMap *I = (PackageMap *)End->UserData;
839 if (I->SrcDone == true)
840 continue;
841 if (I->GenSources(Setup,SrcStats) == false)
842 _error->DumpErrors();
843 }
844
845 delete [] List;
846 }
66905344
DK
847
848 // close the Translation master files
f7f0d6c7 849 for (vector<PackageMap>::reverse_iterator I = PkgList.rbegin(); I != PkgList.rend(); ++I)
66905344
DK
850 if (I->TransWriter != NULL && I->TransWriter->DecreaseRefCounter() == 0)
851 delete I->TransWriter;
852
308b6fc9
DJL
853 return true;
854}
855
856 /*}}}*/
857// DoGenerateContents - Helper for Generate to generate the Contents /*{{{*/
858// ---------------------------------------------------------------------
859static bool DoGenerateContents(Configuration &Setup,
860 vector<PackageMap> &PkgList,
861 CommandLine &CmdL)
862{
c6474fb6 863 c1out << "Packages done, Starting contents." << endl;
b2e465d6
AL
864
865 // Sort the contents file list by date
866 string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
f7f0d6c7 867 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6
AL
868 {
869 struct stat A;
870 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),
871 I->CntCompress,A) == false)
872 time(&I->ContentsMTime);
873 else
874 I->ContentsMTime = A.st_mtime;
875 }
876 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::ContentsCompare());
877
878 /* Now for Contents.. The process here is to do a make-like dependency
879 check. Each contents file is verified to be newer than the package files
880 that describe the debs it indexes. Since the package files contain
881 hashes of the .debs this means they have not changed either so the
882 contents must be up to date. */
883 unsigned long MaxContentsChange = Setup.FindI("Default::MaxContentsChange",UINT_MAX)*1024;
f7f0d6c7 884 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); ++I)
b2e465d6 885 {
1e3f4083 886 // This record is not relevant
b2e465d6
AL
887 if (I->ContentsDone == true ||
888 I->Contents.empty() == true)
889 continue;
890
891 // Do not do everything if the user specified sections.
892 if (CmdL.FileSize() > 2 && I->PkgDone == false)
893 continue;
894
895 struct stat A,B;
896 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),I->CntCompress,A) == true)
897 {
898 if (MultiCompress::GetStat(flCombine(ArchiveDir,I->PkgFile),I->PkgCompress,B) == false)
899 {
dc738e7a 900 _error->Warning(_("Some files are missing in the package file group `%s'"),I->PkgFile.c_str());
b2e465d6
AL
901 continue;
902 }
903
904 if (A.st_mtime > B.st_mtime)
905 continue;
906 }
907
908 if (I->GenContents(Setup,PkgList.begin(),PkgList.end(),
909 MaxContentsChange) == false)
910 _error->DumpErrors();
911
912 // Hit the limit?
913 if (MaxContentsChange == 0)
914 {
915 c1out << "Hit contents update byte limit" << endl;
916 break;
917 }
918 }
308b6fc9
DJL
919
920 return true;
921}
922
923 /*}}}*/
924// Generate - Full generate, using a config file /*{{{*/
925// ---------------------------------------------------------------------
926/* */
927static bool Generate(CommandLine &CmdL)
928{
929 struct CacheDB::Stats SrcStats;
930 if (CmdL.FileSize() < 2)
931 return ShowHelp(CmdL);
932
933 struct timeval StartTime;
934 gettimeofday(&StartTime,0);
935 struct CacheDB::Stats Stats;
b2e465d6 936
308b6fc9
DJL
937 // Read the configuration file.
938 Configuration Setup;
939 if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
940 return false;
941
942 vector<PackageMap> PkgList;
943 LoadTree(PkgList,Setup);
944 LoadBinDir(PkgList,Setup);
945
946 // Sort by cache DB to improve IO locality.
947 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
948 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::SrcDBCompare());
949
950 // Generate packages
951 if (_config->FindB("APT::FTPArchive::ContentsOnly", false) == false)
952 {
953 if(DoGeneratePackagesAndSources(Setup, PkgList, SrcStats, Stats, CmdL) == false)
954 return false;
955 } else {
956 c1out << "Skipping Packages/Sources generation" << endl;
957 }
958
959 // do Contents if needed
960 if (_config->FindB("APT::FTPArchive::Contents", true) == true)
961 if (DoGenerateContents(Setup, PkgList, CmdL) == false)
962 return false;
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
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;
987
988 vector<PackageMap> PkgList;
989 LoadTree(PkgList,Setup);
990 LoadBinDir(PkgList,Setup);
991
992 // Sort by cache DB to improve IO locality.
993 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
53ba4e2c 994 stable_sort(PkgList.begin(),PkgList.end(),PackageMap::SrcDBCompare());
b2e465d6
AL
995
996 string CacheDir = Setup.FindDir("Dir::CacheDir");
997
d935bb17 998 for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); )
b2e465d6 999 {
53ba4e2c
MV
1000 if(I->BinCacheDB != "")
1001 c0out << I->BinCacheDB << endl;
1002 if(I->SrcCacheDB != "")
1003 c0out << I->SrcCacheDB << endl;
b2e465d6 1004 CacheDB DB(flCombine(CacheDir,I->BinCacheDB));
53ba4e2c 1005 CacheDB DB_SRC(flCombine(CacheDir,I->SrcCacheDB));
b2e465d6
AL
1006 if (DB.Clean() == false)
1007 _error->DumpErrors();
53ba4e2c
MV
1008 if (DB_SRC.Clean() == false)
1009 _error->DumpErrors();
b2e465d6
AL
1010
1011 string CacheDB = I->BinCacheDB;
53ba4e2c
MV
1012 string SrcCacheDB = I->SrcCacheDB;
1013 while(I != PkgList.end() &&
1014 I->BinCacheDB == CacheDB &&
1015 I->SrcCacheDB == SrcCacheDB)
1016 ++I;
1017
b2e465d6 1018 }
ce928105 1019
53ba4e2c 1020
b2e465d6
AL
1021 return true;
1022}
1023 /*}}}*/
1024
1025int main(int argc, const char *argv[])
1026{
1692a2c7 1027 setlocale(LC_ALL, "");
b2e465d6
AL
1028 CommandLine::Args Args[] = {
1029 {'h',"help","help",0},
1030 {0,"md5","APT::FTPArchive::MD5",0},
3c54407f
DK
1031 {0,"sha1","APT::FTPArchive::SHA1",0},
1032 {0,"sha256","APT::FTPArchive::SHA256",0},
b2e465d6
AL
1033 {'v',"version","version",0},
1034 {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg},
1035 {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg},
1036 {'q',"quiet","quiet",CommandLine::IntLevel},
1037 {'q',"silent","quiet",CommandLine::IntLevel},
1038 {0,"delink","APT::FTPArchive::DeLinkAct",0},
1039 {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
1040 {0,"contents","APT::FTPArchive::Contents",0},
31981076 1041 {'a',"arch","APT::FTPArchive::Architecture",CommandLine::HasArg},
b2e465d6
AL
1042 {'c',"config-file",0,CommandLine::ConfigFile},
1043 {'o',"option",0,CommandLine::ArbItem},
1044 {0,0,0,0}};
1045 CommandLine::Dispatch Cmds[] = {{"packages",&SimpleGenPackages},
1046 {"contents",&SimpleGenContents},
1047 {"sources",&SimpleGenSources},
98953965 1048 {"release",&SimpleGenRelease},
b2e465d6
AL
1049 {"generate",&Generate},
1050 {"clean",&Clean},
1051 {"help",&ShowHelp},
1052 {0,0}};
1053
1054 // Parse the command line and initialize the package library
1055 CommandLine CmdL(Args,_config);
ad7e0941
DK
1056 ParseCommandLine(CmdL, Cmds, Args, &_config, NULL, argc, argv, ShowHelp);
1057
d9e518c6 1058 _config->CndSet("quiet",0);
b2e465d6 1059 Quiet = _config->FindI("quiet",0);
d9e518c6
DK
1060 InitOutput(clog.rdbuf());
1061
b2e465d6
AL
1062 // Match the operation
1063 CmdL.DispatchArg(Cmds);
1064
1065 if (_error->empty() == false)
1066 {
1067 bool Errors = _error->PendingError();
1068 _error->DumpErrors();
1069 return Errors == true?100:0;
1070 }
1071 return 0;
1072}