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