apt-ftparchive.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: apt-ftparchive.cc,v 1.8.2.3 2004/01/02 22:01:48 mdz Exp $
  4. /* ######################################################################
  5. apt-scanpackages - Efficient work-alike for dpkg-scanpackages
  6. Let contents be disabled from the conf
  7. ##################################################################### */
  8. /*}}}*/
  9. // Include Files /*{{{*/
  10. #ifdef __GNUG__
  11. #pragma implementation "apt-ftparchive.h"
  12. #endif
  13. #include "apt-ftparchive.h"
  14. #include <apt-pkg/error.h>
  15. #include <apt-pkg/configuration.h>
  16. #include <apt-pkg/cmndline.h>
  17. #include <apt-pkg/strutl.h>
  18. #include <config.h>
  19. #include <apti18n.h>
  20. #include <algorithm>
  21. #include <sys/time.h>
  22. #include <regex.h>
  23. #include "contents.h"
  24. #include "multicompress.h"
  25. #include "writer.h"
  26. /*}}}*/
  27. using namespace std;
  28. ostream c0out(0);
  29. ostream c1out(0);
  30. ostream c2out(0);
  31. ofstream devnull("/dev/null");
  32. unsigned Quiet = 0;
  33. // struct PackageMap - List of all package files in the config file /*{{{*/
  34. // ---------------------------------------------------------------------
  35. /* */
  36. struct PackageMap
  37. {
  38. // General Stuff
  39. string BaseDir;
  40. string InternalPrefix;
  41. string FLFile;
  42. string PkgExt;
  43. string SrcExt;
  44. // Stuff for the Package File
  45. string PkgFile;
  46. string BinCacheDB;
  47. string BinOverride;
  48. string ExtraOverride;
  49. // Stuff for the Source File
  50. string SrcFile;
  51. string SrcOverride;
  52. string SrcExtraOverride;
  53. // Contents
  54. string Contents;
  55. string ContentsHead;
  56. // Random things
  57. string Tag;
  58. string PkgCompress;
  59. string CntCompress;
  60. string SrcCompress;
  61. string PathPrefix;
  62. unsigned int DeLinkLimit;
  63. mode_t Permissions;
  64. bool ContentsDone;
  65. bool PkgDone;
  66. bool SrcDone;
  67. time_t ContentsMTime;
  68. struct ContentsCompare : public binary_function<PackageMap,PackageMap,bool>
  69. {
  70. inline bool operator() (const PackageMap &x,const PackageMap &y)
  71. {return x.ContentsMTime < y.ContentsMTime;};
  72. };
  73. struct DBCompare : public binary_function<PackageMap,PackageMap,bool>
  74. {
  75. inline bool operator() (const PackageMap &x,const PackageMap &y)
  76. {return x.BinCacheDB < y.BinCacheDB;};
  77. };
  78. void GetGeneral(Configuration &Setup,Configuration &Block);
  79. bool GenPackages(Configuration &Setup,struct CacheDB::Stats &Stats);
  80. bool GenSources(Configuration &Setup,struct CacheDB::Stats &Stats);
  81. bool GenContents(Configuration &Setup,
  82. vector<PackageMap>::iterator Begin,
  83. vector<PackageMap>::iterator End,
  84. unsigned long &Left);
  85. PackageMap() : DeLinkLimit(0), Permissions(1), ContentsDone(false),
  86. PkgDone(false), SrcDone(false), ContentsMTime(0) {};
  87. };
  88. /*}}}*/
  89. // PackageMap::GetGeneral - Common per-section definitions /*{{{*/
  90. // ---------------------------------------------------------------------
  91. /* */
  92. void PackageMap::GetGeneral(Configuration &Setup,Configuration &Block)
  93. {
  94. PathPrefix = Block.Find("PathPrefix");
  95. if (Block.FindB("External-Links",true) == false)
  96. DeLinkLimit = Setup.FindI("Default::DeLinkLimit",UINT_MAX);
  97. else
  98. DeLinkLimit = 0;
  99. PkgCompress = Block.Find("Packages::Compress",
  100. Setup.Find("Default::Packages::Compress",". gzip").c_str());
  101. CntCompress = Block.Find("Contents::Compress",
  102. Setup.Find("Default::Contents::Compress",". gzip").c_str());
  103. SrcCompress = Block.Find("Sources::Compress",
  104. Setup.Find("Default::Sources::Compress",". gzip").c_str());
  105. SrcExt = Block.Find("Sources::Extensions",
  106. Setup.Find("Default::Sources::Extensions",".dsc").c_str());
  107. PkgExt = Block.Find("Packages::Extensions",
  108. Setup.Find("Default::Packages::Extensions",".deb").c_str());
  109. Permissions = Setup.FindI("Default::FileMode",0644);
  110. if (FLFile.empty() == false)
  111. FLFile = flCombine(Setup.Find("Dir::FileListDir"),FLFile);
  112. if (Contents == " ")
  113. Contents= string();
  114. }
  115. /*}}}*/
  116. // PackageMap::GenPackages - Actually generate a Package file /*{{{*/
  117. // ---------------------------------------------------------------------
  118. /* This generates the Package File described by this object. */
  119. bool PackageMap::GenPackages(Configuration &Setup,struct CacheDB::Stats &Stats)
  120. {
  121. if (PkgFile.empty() == true)
  122. return true;
  123. string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
  124. string OverrideDir = Setup.FindDir("Dir::OverrideDir");
  125. string CacheDir = Setup.FindDir("Dir::CacheDir");
  126. struct timeval StartTime;
  127. gettimeofday(&StartTime,0);
  128. PkgDone = true;
  129. // Create a package writer object.
  130. PackagesWriter Packages(flCombine(CacheDir,BinCacheDB),
  131. flCombine(OverrideDir,BinOverride),
  132. flCombine(OverrideDir,ExtraOverride));
  133. if (PkgExt.empty() == false && Packages.SetExts(PkgExt) == false)
  134. return _error->Error(_("Package extension list is too long"));
  135. if (_error->PendingError() == true)
  136. return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
  137. Packages.PathPrefix = PathPrefix;
  138. Packages.DirStrip = ArchiveDir;
  139. Packages.InternalPrefix = flCombine(ArchiveDir,InternalPrefix);
  140. Packages.Stats.DeLinkBytes = Stats.DeLinkBytes;
  141. Packages.DeLinkLimit = DeLinkLimit;
  142. // Create a compressor object
  143. MultiCompress Comp(flCombine(ArchiveDir,PkgFile),
  144. PkgCompress,Permissions);
  145. Packages.Output = Comp.Input;
  146. if (_error->PendingError() == true)
  147. return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
  148. c0out << ' ' << BaseDir << ":" << flush;
  149. // Do recursive directory searching
  150. if (FLFile.empty() == true)
  151. {
  152. if (Packages.RecursiveScan(flCombine(ArchiveDir,BaseDir)) == false)
  153. return false;
  154. }
  155. else
  156. {
  157. if (Packages.LoadFileList(ArchiveDir,FLFile) == false)
  158. return false;
  159. }
  160. Packages.Output = 0; // Just in case
  161. // Finish compressing
  162. unsigned long Size;
  163. if (Comp.Finalize(Size) == false)
  164. {
  165. c0out << endl;
  166. return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
  167. }
  168. if (Size != 0)
  169. c0out << " New "
  170. << SizeToStr(Size) << "B ";
  171. else
  172. c0out << ' ';
  173. struct timeval NewTime;
  174. gettimeofday(&NewTime,0);
  175. double Delta = NewTime.tv_sec - StartTime.tv_sec +
  176. (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
  177. c0out << Packages.Stats.Packages << " files " <<
  178. /* SizeToStr(Packages.Stats.MD5Bytes) << "B/" << */
  179. SizeToStr(Packages.Stats.Bytes) << "B " <<
  180. TimeToStr((long)Delta) << endl;
  181. Stats.Add(Packages.Stats);
  182. Stats.DeLinkBytes = Packages.Stats.DeLinkBytes;
  183. return !_error->PendingError();
  184. }
  185. /*}}}*/
  186. // PackageMap::GenSources - Actually generate a Source file /*{{{*/
  187. // ---------------------------------------------------------------------
  188. /* This generates the Sources File described by this object. */
  189. bool PackageMap::GenSources(Configuration &Setup,struct CacheDB::Stats &Stats)
  190. {
  191. if (SrcFile.empty() == true)
  192. return true;
  193. string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
  194. string OverrideDir = Setup.FindDir("Dir::OverrideDir");
  195. string CacheDir = Setup.FindDir("Dir::CacheDir");
  196. struct timeval StartTime;
  197. gettimeofday(&StartTime,0);
  198. SrcDone = true;
  199. // Create a package writer object.
  200. SourcesWriter Sources(flCombine(OverrideDir,BinOverride),
  201. flCombine(OverrideDir,SrcOverride),
  202. flCombine(OverrideDir,SrcExtraOverride));
  203. if (SrcExt.empty() == false && Sources.SetExts(SrcExt) == false)
  204. return _error->Error(_("Source extension list is too long"));
  205. if (_error->PendingError() == true)
  206. return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
  207. Sources.PathPrefix = PathPrefix;
  208. Sources.DirStrip = ArchiveDir;
  209. Sources.InternalPrefix = flCombine(ArchiveDir,InternalPrefix);
  210. Sources.DeLinkLimit = DeLinkLimit;
  211. Sources.Stats.DeLinkBytes = Stats.DeLinkBytes;
  212. // Create a compressor object
  213. MultiCompress Comp(flCombine(ArchiveDir,SrcFile),
  214. SrcCompress,Permissions);
  215. Sources.Output = Comp.Input;
  216. if (_error->PendingError() == true)
  217. return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
  218. c0out << ' ' << BaseDir << ":" << flush;
  219. // Do recursive directory searching
  220. if (FLFile.empty() == true)
  221. {
  222. if (Sources.RecursiveScan(flCombine(ArchiveDir,BaseDir))== false)
  223. return false;
  224. }
  225. else
  226. {
  227. if (Sources.LoadFileList(ArchiveDir,FLFile) == false)
  228. return false;
  229. }
  230. Sources.Output = 0; // Just in case
  231. // Finish compressing
  232. unsigned long Size;
  233. if (Comp.Finalize(Size) == false)
  234. {
  235. c0out << endl;
  236. return _error->Error(_("Error processing directory %s"),BaseDir.c_str());
  237. }
  238. if (Size != 0)
  239. c0out << " New "
  240. << SizeToStr(Size) << "B ";
  241. else
  242. c0out << ' ';
  243. struct timeval NewTime;
  244. gettimeofday(&NewTime,0);
  245. double Delta = NewTime.tv_sec - StartTime.tv_sec +
  246. (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
  247. c0out << Sources.Stats.Packages << " pkgs in " <<
  248. TimeToStr((long)Delta) << endl;
  249. Stats.Add(Sources.Stats);
  250. Stats.DeLinkBytes = Sources.Stats.DeLinkBytes;
  251. return !_error->PendingError();
  252. }
  253. /*}}}*/
  254. // PackageMap::GenContents - Actually generate a Contents file /*{{{*/
  255. // ---------------------------------------------------------------------
  256. /* This generates the contents file partially described by this object.
  257. It searches the given iterator range for other package files that map
  258. into this contents file and includes their data as well when building. */
  259. bool PackageMap::GenContents(Configuration &Setup,
  260. vector<PackageMap>::iterator Begin,
  261. vector<PackageMap>::iterator End,
  262. unsigned long &Left)
  263. {
  264. if (Contents.empty() == true)
  265. return true;
  266. if (Left == 0)
  267. return true;
  268. string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
  269. string CacheDir = Setup.FindDir("Dir::CacheDir");
  270. string OverrideDir = Setup.FindDir("Dir::OverrideDir");
  271. struct timeval StartTime;
  272. gettimeofday(&StartTime,0);
  273. // Create a package writer object.
  274. ContentsWriter Contents("");
  275. if (PkgExt.empty() == false && Contents.SetExts(PkgExt) == false)
  276. return _error->Error(_("Package extension list is too long"));
  277. if (_error->PendingError() == true)
  278. return false;
  279. MultiCompress Comp(flCombine(ArchiveDir,this->Contents),
  280. CntCompress,Permissions);
  281. Comp.UpdateMTime = Setup.FindI("Default::ContentsAge",10)*24*60*60;
  282. Contents.Output = Comp.Input;
  283. if (_error->PendingError() == true)
  284. return false;
  285. // Write the header out.
  286. if (ContentsHead.empty() == false)
  287. {
  288. FileFd Head(flCombine(OverrideDir,ContentsHead),FileFd::ReadOnly);
  289. if (_error->PendingError() == true)
  290. return false;
  291. unsigned long Size = Head.Size();
  292. unsigned char Buf[4096];
  293. while (Size != 0)
  294. {
  295. unsigned long ToRead = Size;
  296. if (Size > sizeof(Buf))
  297. ToRead = sizeof(Buf);
  298. if (Head.Read(Buf,ToRead) == false)
  299. return false;
  300. if (fwrite(Buf,1,ToRead,Comp.Input) != ToRead)
  301. return _error->Errno("fwrite",_("Error writing header to contents file"));
  302. Size -= ToRead;
  303. }
  304. }
  305. /* Go over all the package file records and parse all the package
  306. files associated with this contents file into one great big honking
  307. memory structure, then dump the sorted version */
  308. c0out << ' ' << this->Contents << ":" << flush;
  309. for (vector<PackageMap>::iterator I = Begin; I != End; I++)
  310. {
  311. if (I->Contents != this->Contents)
  312. continue;
  313. Contents.Prefix = ArchiveDir;
  314. Contents.ReadyDB(flCombine(CacheDir,I->BinCacheDB));
  315. Contents.ReadFromPkgs(flCombine(ArchiveDir,I->PkgFile),
  316. I->PkgCompress);
  317. I->ContentsDone = true;
  318. }
  319. Contents.Finish();
  320. // Finish compressing
  321. unsigned long Size;
  322. if (Comp.Finalize(Size) == false || _error->PendingError() == true)
  323. {
  324. c0out << endl;
  325. return _error->Error(_("Error processing contents %s"),
  326. this->Contents.c_str());
  327. }
  328. if (Size != 0)
  329. {
  330. c0out << " New " << SizeToStr(Size) << "B ";
  331. if (Left > Size)
  332. Left -= Size;
  333. else
  334. Left = 0;
  335. }
  336. else
  337. c0out << ' ';
  338. struct timeval NewTime;
  339. gettimeofday(&NewTime,0);
  340. double Delta = NewTime.tv_sec - StartTime.tv_sec +
  341. (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
  342. c0out << Contents.Stats.Packages << " files " <<
  343. SizeToStr(Contents.Stats.Bytes) << "B " <<
  344. TimeToStr((long)Delta) << endl;
  345. return true;
  346. }
  347. /*}}}*/
  348. // LoadTree - Load a 'tree' section from the Generate Config /*{{{*/
  349. // ---------------------------------------------------------------------
  350. /* This populates the PkgList with all the possible permutations of the
  351. section/arch lists. */
  352. void LoadTree(vector<PackageMap> &PkgList,Configuration &Setup)
  353. {
  354. // Load the defaults
  355. string DDir = Setup.Find("TreeDefault::Directory",
  356. "$(DIST)/$(SECTION)/binary-$(ARCH)/");
  357. string DSDir = Setup.Find("TreeDefault::SrcDirectory",
  358. "$(DIST)/$(SECTION)/source/");
  359. string DPkg = Setup.Find("TreeDefault::Packages",
  360. "$(DIST)/$(SECTION)/binary-$(ARCH)/Packages");
  361. string DIPrfx = Setup.Find("TreeDefault::InternalPrefix",
  362. "$(DIST)/$(SECTION)/");
  363. string DContents = Setup.Find("TreeDefault::Contents",
  364. "$(DIST)/Contents-$(ARCH)");
  365. string DContentsH = Setup.Find("TreeDefault::Contents::Header","");
  366. string DBCache = Setup.Find("TreeDefault::BinCacheDB",
  367. "packages-$(ARCH).db");
  368. string DSources = Setup.Find("TreeDefault::Sources",
  369. "$(DIST)/$(SECTION)/source/Sources");
  370. string DFLFile = Setup.Find("TreeDefault::FileList", "");
  371. string DSFLFile = Setup.Find("TreeDefault::SourceFileList", "");
  372. // Process 'tree' type sections
  373. const Configuration::Item *Top = Setup.Tree("tree");
  374. for (Top = (Top == 0?0:Top->Child); Top != 0;)
  375. {
  376. Configuration Block(Top);
  377. string Dist = Top->Tag;
  378. // Parse the sections
  379. string Tmp = Block.Find("Sections");
  380. const char *Sections = Tmp.c_str();
  381. string Section;
  382. while (ParseQuoteWord(Sections,Section) == true)
  383. {
  384. string Tmp2 = Block.Find("Architectures");
  385. string Arch;
  386. const char *Archs = Tmp2.c_str();
  387. while (ParseQuoteWord(Archs,Arch) == true)
  388. {
  389. struct SubstVar Vars[] = {{"$(DIST)",&Dist},
  390. {"$(SECTION)",&Section},
  391. {"$(ARCH)",&Arch},
  392. {}};
  393. PackageMap Itm;
  394. Itm.BinOverride = SubstVar(Block.Find("BinOverride"),Vars);
  395. Itm.InternalPrefix = SubstVar(Block.Find("InternalPrefix",DIPrfx.c_str()),Vars);
  396. if (stringcasecmp(Arch,"source") == 0)
  397. {
  398. Itm.SrcOverride = SubstVar(Block.Find("SrcOverride"),Vars);
  399. Itm.BaseDir = SubstVar(Block.Find("SrcDirectory",DSDir.c_str()),Vars);
  400. Itm.SrcFile = SubstVar(Block.Find("Sources",DSources.c_str()),Vars);
  401. Itm.Tag = SubstVar("$(DIST)/$(SECTION)/source",Vars);
  402. Itm.FLFile = SubstVar(Block.Find("SourceFileList",DSFLFile.c_str()),Vars);
  403. Itm.SrcExtraOverride = SubstVar(Block.Find("SrcExtraOverride"),Vars);
  404. }
  405. else
  406. {
  407. Itm.BinCacheDB = SubstVar(Block.Find("BinCacheDB",DBCache.c_str()),Vars);
  408. Itm.BaseDir = SubstVar(Block.Find("Directory",DDir.c_str()),Vars);
  409. Itm.PkgFile = SubstVar(Block.Find("Packages",DPkg.c_str()),Vars);
  410. Itm.Tag = SubstVar("$(DIST)/$(SECTION)/$(ARCH)",Vars);
  411. Itm.Contents = SubstVar(Block.Find("Contents",DContents.c_str()),Vars);
  412. Itm.ContentsHead = SubstVar(Block.Find("Contents::Header",DContentsH.c_str()),Vars);
  413. Itm.FLFile = SubstVar(Block.Find("FileList",DFLFile.c_str()),Vars);
  414. Itm.ExtraOverride = SubstVar(Block.Find("ExtraOverride"),Vars);
  415. }
  416. Itm.GetGeneral(Setup,Block);
  417. PkgList.push_back(Itm);
  418. }
  419. }
  420. Top = Top->Next;
  421. }
  422. }
  423. /*}}}*/
  424. // LoadBinDir - Load a 'bindirectory' section from the Generate Config /*{{{*/
  425. // ---------------------------------------------------------------------
  426. /* */
  427. void LoadBinDir(vector<PackageMap> &PkgList,Configuration &Setup)
  428. {
  429. // Process 'bindirectory' type sections
  430. const Configuration::Item *Top = Setup.Tree("bindirectory");
  431. for (Top = (Top == 0?0:Top->Child); Top != 0;)
  432. {
  433. Configuration Block(Top);
  434. PackageMap Itm;
  435. Itm.PkgFile = Block.Find("Packages");
  436. Itm.SrcFile = Block.Find("Sources");
  437. Itm.BinCacheDB = Block.Find("BinCacheDB");
  438. Itm.BinOverride = Block.Find("BinOverride");
  439. Itm.ExtraOverride = Block.Find("ExtraOverride");
  440. Itm.SrcExtraOverride = Block.Find("SrcExtraOverride");
  441. Itm.SrcOverride = Block.Find("SrcOverride");
  442. Itm.BaseDir = Top->Tag;
  443. Itm.FLFile = Block.Find("FileList");
  444. Itm.InternalPrefix = Block.Find("InternalPrefix",Top->Tag.c_str());
  445. Itm.Contents = Block.Find("Contents");
  446. Itm.ContentsHead = Block.Find("Contents::Header");
  447. Itm.GetGeneral(Setup,Block);
  448. PkgList.push_back(Itm);
  449. Top = Top->Next;
  450. }
  451. }
  452. /*}}}*/
  453. // ShowHelp - Show the help text /*{{{*/
  454. // ---------------------------------------------------------------------
  455. /* */
  456. bool ShowHelp(CommandLine &CmdL)
  457. {
  458. ioprintf(cout,_("%s %s for %s %s compiled on %s %s\n"),PACKAGE,VERSION,
  459. COMMON_OS,COMMON_CPU,__DATE__,__TIME__);
  460. if (_config->FindB("version") == true)
  461. return true;
  462. cout <<
  463. _("Usage: apt-ftparchive [options] command\n"
  464. "Commands: packages binarypath [overridefile [pathprefix]]\n"
  465. " sources srcpath [overridefile [pathprefix]]\n"
  466. " contents path\n"
  467. " release path\n"
  468. " generate config [groups]\n"
  469. " clean config\n"
  470. "\n"
  471. "apt-ftparchive generates index files for Debian archives. It supports\n"
  472. "many styles of generation from fully automated to functional replacements\n"
  473. "for dpkg-scanpackages and dpkg-scansources\n"
  474. "\n"
  475. "apt-ftparchive generates Package files from a tree of .debs. The\n"
  476. "Package file contains the contents of all the control fields from\n"
  477. "each package as well as the MD5 hash and filesize. An override file\n"
  478. "is supported to force the value of Priority and Section.\n"
  479. "\n"
  480. "Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n"
  481. "The --source-override option can be used to specify a src override file\n"
  482. "\n"
  483. "The 'packages' and 'sources' command should be run in the root of the\n"
  484. "tree. BinaryPath should point to the base of the recursive search and \n"
  485. "override file should contain the override flags. Pathprefix is\n"
  486. "appended to the filename fields if present. Example usage from the \n"
  487. "Debian archive:\n"
  488. " apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n"
  489. " dists/potato/main/binary-i386/Packages\n"
  490. "\n"
  491. "Options:\n"
  492. " -h This help text\n"
  493. " --md5 Control MD5 generation\n"
  494. " -s=? Source override file\n"
  495. " -q Quiet\n"
  496. " -d=? Select the optional caching database\n"
  497. " --no-delink Enable delinking debug mode\n"
  498. " --contents Control contents file generation\n"
  499. " -c=? Read this configuration file\n"
  500. " -o=? Set an arbitary configuration option") << endl;
  501. return true;
  502. }
  503. /*}}}*/
  504. // SimpleGenPackages - Generate a Packages file for a directory tree /*{{{*/
  505. // ---------------------------------------------------------------------
  506. /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
  507. bool SimpleGenPackages(CommandLine &CmdL)
  508. {
  509. if (CmdL.FileSize() < 2)
  510. return ShowHelp(CmdL);
  511. string Override;
  512. if (CmdL.FileSize() >= 3)
  513. Override = CmdL.FileList[2];
  514. // Create a package writer object.
  515. PackagesWriter Packages(_config->Find("APT::FTPArchive::DB"),
  516. Override, "");
  517. if (_error->PendingError() == true)
  518. return false;
  519. if (CmdL.FileSize() >= 4)
  520. Packages.PathPrefix = CmdL.FileList[3];
  521. // Do recursive directory searching
  522. if (Packages.RecursiveScan(CmdL.FileList[1]) == false)
  523. return false;
  524. return true;
  525. }
  526. /*}}}*/
  527. // SimpleGenContents - Generate a Contents listing /*{{{*/
  528. // ---------------------------------------------------------------------
  529. /* */
  530. bool SimpleGenContents(CommandLine &CmdL)
  531. {
  532. if (CmdL.FileSize() < 2)
  533. return ShowHelp(CmdL);
  534. // Create a package writer object.
  535. ContentsWriter Contents(_config->Find("APT::FTPArchive::DB"));
  536. if (_error->PendingError() == true)
  537. return false;
  538. // Do recursive directory searching
  539. if (Contents.RecursiveScan(CmdL.FileList[1]) == false)
  540. return false;
  541. Contents.Finish();
  542. return true;
  543. }
  544. /*}}}*/
  545. // SimpleGenSources - Generate a Sources file for a directory tree /*{{{*/
  546. // ---------------------------------------------------------------------
  547. /* This emulates dpkg-scanpackages's command line interface. 'mostly' */
  548. bool SimpleGenSources(CommandLine &CmdL)
  549. {
  550. if (CmdL.FileSize() < 2)
  551. return ShowHelp(CmdL);
  552. string Override;
  553. if (CmdL.FileSize() >= 3)
  554. Override = CmdL.FileList[2];
  555. string SOverride;
  556. if (Override.empty() == false)
  557. SOverride = Override + ".src";
  558. SOverride = _config->Find("APT::FTPArchive::SourceOverride",
  559. SOverride.c_str());
  560. // Create a package writer object.
  561. SourcesWriter Sources(Override,SOverride);
  562. if (_error->PendingError() == true)
  563. return false;
  564. if (CmdL.FileSize() >= 4)
  565. Sources.PathPrefix = CmdL.FileList[3];
  566. // Do recursive directory searching
  567. if (Sources.RecursiveScan(CmdL.FileList[1]) == false)
  568. return false;
  569. return true;
  570. }
  571. /*}}}*/
  572. // SimpleGenRelease - Generate a Release file for a directory tree /*{{{*/
  573. // ---------------------------------------------------------------------
  574. bool SimpleGenRelease(CommandLine &CmdL)
  575. {
  576. if (CmdL.FileSize() < 2)
  577. return ShowHelp(CmdL);
  578. string Dir = CmdL.FileList[1];
  579. ReleaseWriter Release("");
  580. Release.DirStrip = Dir;
  581. if (_error->PendingError() == true)
  582. return false;
  583. if (Release.RecursiveScan(Dir) == false)
  584. return false;
  585. Release.Finish();
  586. return true;
  587. }
  588. /*}}}*/
  589. // Generate - Full generate, using a config file /*{{{*/
  590. // ---------------------------------------------------------------------
  591. /* */
  592. bool Generate(CommandLine &CmdL)
  593. {
  594. struct CacheDB::Stats SrcStats;
  595. if (CmdL.FileSize() < 2)
  596. return ShowHelp(CmdL);
  597. struct timeval StartTime;
  598. gettimeofday(&StartTime,0);
  599. struct CacheDB::Stats Stats;
  600. // Read the configuration file.
  601. Configuration Setup;
  602. if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
  603. return false;
  604. vector<PackageMap> PkgList;
  605. LoadTree(PkgList,Setup);
  606. LoadBinDir(PkgList,Setup);
  607. // Sort by cache DB to improve IO locality.
  608. stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
  609. // Generate packages
  610. if (CmdL.FileSize() <= 2)
  611. {
  612. for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
  613. if (I->GenPackages(Setup,Stats) == false)
  614. _error->DumpErrors();
  615. for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
  616. if (I->GenSources(Setup,SrcStats) == false)
  617. _error->DumpErrors();
  618. }
  619. else
  620. {
  621. // Make a choice list out of the package list..
  622. RxChoiceList *List = new RxChoiceList[2*PkgList.size()+1];
  623. RxChoiceList *End = List;
  624. for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
  625. {
  626. End->UserData = &(*I);
  627. End->Str = I->BaseDir.c_str();
  628. End++;
  629. End->UserData = &(*I);
  630. End->Str = I->Tag.c_str();
  631. End++;
  632. }
  633. End->Str = 0;
  634. // Regex it
  635. if (RegexChoice(List,CmdL.FileList + 2,CmdL.FileList + CmdL.FileSize()) == 0)
  636. {
  637. delete [] List;
  638. return _error->Error(_("No selections matched"));
  639. }
  640. _error->DumpErrors();
  641. // Do the generation for Packages
  642. for (End = List; End->Str != 0; End++)
  643. {
  644. if (End->Hit == false)
  645. continue;
  646. PackageMap *I = (PackageMap *)End->UserData;
  647. if (I->PkgDone == true)
  648. continue;
  649. if (I->GenPackages(Setup,Stats) == false)
  650. _error->DumpErrors();
  651. }
  652. // Do the generation for Sources
  653. for (End = List; End->Str != 0; End++)
  654. {
  655. if (End->Hit == false)
  656. continue;
  657. PackageMap *I = (PackageMap *)End->UserData;
  658. if (I->SrcDone == true)
  659. continue;
  660. if (I->GenSources(Setup,SrcStats) == false)
  661. _error->DumpErrors();
  662. }
  663. delete [] List;
  664. }
  665. if (_config->FindB("APT::FTPArchive::Contents",true) == false)
  666. return true;
  667. c1out << "Done Packages, Starting contents." << endl;
  668. // Sort the contents file list by date
  669. string ArchiveDir = Setup.FindDir("Dir::ArchiveDir");
  670. for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
  671. {
  672. struct stat A;
  673. if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),
  674. I->CntCompress,A) == false)
  675. time(&I->ContentsMTime);
  676. else
  677. I->ContentsMTime = A.st_mtime;
  678. }
  679. stable_sort(PkgList.begin(),PkgList.end(),PackageMap::ContentsCompare());
  680. /* Now for Contents.. The process here is to do a make-like dependency
  681. check. Each contents file is verified to be newer than the package files
  682. that describe the debs it indexes. Since the package files contain
  683. hashes of the .debs this means they have not changed either so the
  684. contents must be up to date. */
  685. unsigned long MaxContentsChange = Setup.FindI("Default::MaxContentsChange",UINT_MAX)*1024;
  686. for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); I++)
  687. {
  688. // This record is not relevent
  689. if (I->ContentsDone == true ||
  690. I->Contents.empty() == true)
  691. continue;
  692. // Do not do everything if the user specified sections.
  693. if (CmdL.FileSize() > 2 && I->PkgDone == false)
  694. continue;
  695. struct stat A,B;
  696. if (MultiCompress::GetStat(flCombine(ArchiveDir,I->Contents),I->CntCompress,A) == true)
  697. {
  698. if (MultiCompress::GetStat(flCombine(ArchiveDir,I->PkgFile),I->PkgCompress,B) == false)
  699. {
  700. _error->Warning(_("Some files are missing in the package file group `%s'"),I->PkgFile.c_str());
  701. continue;
  702. }
  703. if (A.st_mtime > B.st_mtime)
  704. continue;
  705. }
  706. if (I->GenContents(Setup,PkgList.begin(),PkgList.end(),
  707. MaxContentsChange) == false)
  708. _error->DumpErrors();
  709. // Hit the limit?
  710. if (MaxContentsChange == 0)
  711. {
  712. c1out << "Hit contents update byte limit" << endl;
  713. break;
  714. }
  715. }
  716. struct timeval NewTime;
  717. gettimeofday(&NewTime,0);
  718. double Delta = NewTime.tv_sec - StartTime.tv_sec +
  719. (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
  720. c1out << "Done. " << SizeToStr(Stats.Bytes) << "B in " << Stats.Packages
  721. << " archives. Took " << TimeToStr((long)Delta) << endl;
  722. return true;
  723. }
  724. /*}}}*/
  725. // Clean - Clean out the databases /*{{{*/
  726. // ---------------------------------------------------------------------
  727. /* */
  728. bool Clean(CommandLine &CmdL)
  729. {
  730. if (CmdL.FileSize() != 2)
  731. return ShowHelp(CmdL);
  732. // Read the configuration file.
  733. Configuration Setup;
  734. if (ReadConfigFile(Setup,CmdL.FileList[1],true) == false)
  735. return false;
  736. vector<PackageMap> PkgList;
  737. LoadTree(PkgList,Setup);
  738. LoadBinDir(PkgList,Setup);
  739. // Sort by cache DB to improve IO locality.
  740. stable_sort(PkgList.begin(),PkgList.end(),PackageMap::DBCompare());
  741. string CacheDir = Setup.FindDir("Dir::CacheDir");
  742. for (vector<PackageMap>::iterator I = PkgList.begin(); I != PkgList.end(); )
  743. {
  744. c0out << I->BinCacheDB << endl;
  745. CacheDB DB(flCombine(CacheDir,I->BinCacheDB));
  746. if (DB.Clean() == false)
  747. _error->DumpErrors();
  748. string CacheDB = I->BinCacheDB;
  749. for (; I != PkgList.end() && I->BinCacheDB == CacheDB; I++);
  750. }
  751. return true;
  752. }
  753. /*}}}*/
  754. int main(int argc, const char *argv[])
  755. {
  756. CommandLine::Args Args[] = {
  757. {'h',"help","help",0},
  758. {0,"md5","APT::FTPArchive::MD5",0},
  759. {'v',"version","version",0},
  760. {'d',"db","APT::FTPArchive::DB",CommandLine::HasArg},
  761. {'s',"source-override","APT::FTPArchive::SourceOverride",CommandLine::HasArg},
  762. {'q',"quiet","quiet",CommandLine::IntLevel},
  763. {'q',"silent","quiet",CommandLine::IntLevel},
  764. {0,"delink","APT::FTPArchive::DeLinkAct",0},
  765. {0,"readonly","APT::FTPArchive::ReadOnlyDB",0},
  766. {0,"contents","APT::FTPArchive::Contents",0},
  767. {'c',"config-file",0,CommandLine::ConfigFile},
  768. {'o',"option",0,CommandLine::ArbItem},
  769. {0,0,0,0}};
  770. CommandLine::Dispatch Cmds[] = {{"packages",&SimpleGenPackages},
  771. {"contents",&SimpleGenContents},
  772. {"sources",&SimpleGenSources},
  773. {"release",&SimpleGenRelease},
  774. {"generate",&Generate},
  775. {"clean",&Clean},
  776. {"help",&ShowHelp},
  777. {0,0}};
  778. // Parse the command line and initialize the package library
  779. CommandLine CmdL(Args,_config);
  780. if (CmdL.Parse(argc,argv) == false)
  781. {
  782. _error->DumpErrors();
  783. return 100;
  784. }
  785. // See if the help should be shown
  786. if (_config->FindB("help") == true ||
  787. _config->FindB("version") == true ||
  788. CmdL.FileSize() == 0)
  789. {
  790. ShowHelp(CmdL);
  791. return 0;
  792. }
  793. // Setup the output streams
  794. c0out.rdbuf(clog.rdbuf());
  795. c1out.rdbuf(clog.rdbuf());
  796. c2out.rdbuf(clog.rdbuf());
  797. Quiet = _config->FindI("quiet",0);
  798. if (Quiet > 0)
  799. c0out.rdbuf(devnull.rdbuf());
  800. if (Quiet > 1)
  801. c1out.rdbuf(devnull.rdbuf());
  802. // Match the operation
  803. CmdL.DispatchArg(Cmds);
  804. if (_error->empty() == false)
  805. {
  806. bool Errors = _error->PendingError();
  807. _error->DumpErrors();
  808. return Errors == true?100:0;
  809. }
  810. return 0;
  811. }