]> git.saurik.com Git - apt.git/blame_incremental - ftparchive/writer.cc
Add a vcg command to apt-cache, similiar to dotty.
[apt.git] / ftparchive / writer.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: writer.cc,v 1.7 2003/02/10 07:34:41 doogie Exp $
4/* ######################################################################
5
6 Writer
7
8 The file writer classes. These write various types of output, sources,
9 packages and contents.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
14#ifdef __GNUG__
15#pragma implementation "writer.h"
16#endif
17
18#include "writer.h"
19
20#include <apti18n.h>
21#include <apt-pkg/strutl.h>
22#include <apt-pkg/error.h>
23#include <apt-pkg/configuration.h>
24#include <apt-pkg/md5.h>
25#include <apt-pkg/deblistparser.h>
26
27#include <sys/types.h>
28#include <unistd.h>
29#include <ftw.h>
30#include <iostream>
31
32#include "cachedb.h"
33#include "apt-ftparchive.h"
34#include "multicompress.h"
35 /*}}}*/
36
37using namespace std;
38FTWScanner *FTWScanner::Owner;
39
40// SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
41// ---------------------------------------------------------------------
42/* */
43inline void SetTFRewriteData(struct TFRewriteData &tfrd,
44 const char *tag,
45 const char *rewrite,
46 const char *newtag = 0)
47{
48 tfrd.Tag = tag;
49 tfrd.Rewrite = rewrite;
50 tfrd.NewTag = newtag;
51}
52 /*}}}*/
53
54// FTWScanner::FTWScanner - Constructor /*{{{*/
55// ---------------------------------------------------------------------
56/* */
57FTWScanner::FTWScanner()
58{
59 ErrorPrinted = false;
60 NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
61 TmpExt = 0;
62 Ext[0] = 0;
63 RealPath = 0;
64 long PMax = pathconf(".",_PC_PATH_MAX);
65 if (PMax > 0)
66 RealPath = new char[PMax];
67}
68 /*}}}*/
69// FTWScanner::Scanner - FTW Scanner /*{{{*/
70// ---------------------------------------------------------------------
71/* This is the FTW scanner, it processes each directory element in the
72 directory tree. */
73int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
74{
75 if (Flag == FTW_DNR)
76 {
77 Owner->NewLine(1);
78 ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
79 }
80 if (Flag == FTW_NS)
81 {
82 Owner->NewLine(1);
83 ioprintf(c1out, _("W: Unable to stat %s\n"), File);
84 }
85 if (Flag != FTW_F)
86 return 0;
87
88 // See if it is a .deb
89 if (strlen(File) < 4)
90 return 0;
91
92 unsigned CurExt = 0;
93 for (; Owner->Ext[CurExt] != 0; CurExt++)
94 if (strcmp(File+strlen(File)-strlen(Owner->Ext[CurExt]),
95 Owner->Ext[CurExt]) == 0)
96 break;
97 if (Owner->Ext[CurExt] == 0)
98 return 0;
99
100 /* Process it. If the file is a link then resolve it into an absolute
101 name.. This works best if the directory components the scanner are
102 given are not links themselves. */
103 char Jnk[2];
104 Owner->OriginalPath = File;
105 if (Owner->RealPath != 0 && readlink(File,Jnk,sizeof(Jnk)) != -1 &&
106 realpath(File,Owner->RealPath) != 0)
107 Owner->DoPackage(Owner->RealPath);
108 else
109 Owner->DoPackage(File);
110
111 if (_error->empty() == false)
112 {
113 // Print any errors or warnings found
114 string Err;
115 bool SeenPath = false;
116 while (_error->empty() == false)
117 {
118 Owner->NewLine(1);
119
120 bool Type = _error->PopMessage(Err);
121 if (Type == true)
122 cerr << _("E: ") << Err << endl;
123 else
124 cerr << _("W: ") << Err << endl;
125
126 if (Err.find(File) != string::npos)
127 SeenPath = true;
128 }
129
130 if (SeenPath == false)
131 cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
132 return 0;
133 }
134
135 return 0;
136}
137 /*}}}*/
138// FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
139// ---------------------------------------------------------------------
140/* */
141bool FTWScanner::RecursiveScan(string Dir)
142{
143 /* If noprefix is set then jam the scan root in, so we don't generate
144 link followed paths out of control */
145 if (InternalPrefix.empty() == true)
146 {
147 if (realpath(Dir.c_str(),RealPath) == 0)
148 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
149 InternalPrefix = RealPath;
150 }
151
152 // Do recursive directory searching
153 Owner = this;
154 int Res = ftw(Dir.c_str(),Scanner,30);
155
156 // Error treewalking?
157 if (Res != 0)
158 {
159 if (_error->PendingError() == false)
160 _error->Errno("ftw",_("Tree walking failed"));
161 return false;
162 }
163
164 return true;
165}
166 /*}}}*/
167// FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
168// ---------------------------------------------------------------------
169/* This is an alternative to using FTW to locate files, it reads the list
170 of files from another file. */
171bool FTWScanner::LoadFileList(string Dir,string File)
172{
173 /* If noprefix is set then jam the scan root in, so we don't generate
174 link followed paths out of control */
175 if (InternalPrefix.empty() == true)
176 {
177 if (realpath(Dir.c_str(),RealPath) == 0)
178 return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
179 InternalPrefix = RealPath;
180 }
181
182 Owner = this;
183 FILE *List = fopen(File.c_str(),"r");
184 if (List == 0)
185 return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
186
187 /* We are a tad tricky here.. We prefix the buffer with the directory
188 name, that way if we need a full path with just use line.. Sneaky and
189 fully evil. */
190 char Line[1000];
191 char *FileStart;
192 if (Dir.empty() == true || Dir.end()[-1] != '/')
193 FileStart = Line + snprintf(Line,sizeof(Line),"%s/",Dir.c_str());
194 else
195 FileStart = Line + snprintf(Line,sizeof(Line),"%s",Dir.c_str());
196 while (fgets(FileStart,sizeof(Line) - (FileStart - Line),List) != 0)
197 {
198 char *FileName = _strstrip(FileStart);
199 if (FileName[0] == 0)
200 continue;
201
202 if (FileName[0] != '/')
203 {
204 if (FileName != FileStart)
205 memmove(FileStart,FileName,strlen(FileStart));
206 FileName = Line;
207 }
208
209 struct stat St;
210 int Flag = FTW_F;
211 if (stat(FileName,&St) != 0)
212 Flag = FTW_NS;
213
214 if (Scanner(FileName,&St,Flag) != 0)
215 break;
216 }
217
218 fclose(List);
219 return true;
220}
221 /*}}}*/
222// FTWScanner::Delink - Delink symlinks /*{{{*/
223// ---------------------------------------------------------------------
224/* */
225bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
226 unsigned long &DeLinkBytes,
227 struct stat &St)
228{
229 // See if this isn't an internaly prefix'd file name.
230 if (InternalPrefix.empty() == false &&
231 InternalPrefix.length() < FileName.length() &&
232 stringcmp(FileName.begin(),FileName.begin() + InternalPrefix.length(),
233 InternalPrefix.begin(),InternalPrefix.end()) != 0)
234 {
235 if (DeLinkLimit != 0 && DeLinkBytes/1024 < DeLinkLimit)
236 {
237 // Tidy up the display
238 if (DeLinkBytes == 0)
239 cout << endl;
240
241 NewLine(1);
242 ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
243 SizeToStr(St.st_size).c_str());
244 c1out << flush;
245
246 if (NoLinkAct == false)
247 {
248 char OldLink[400];
249 if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
250 _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
251 else
252 {
253 if (unlink(OriginalPath) != 0)
254 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
255 else
256 {
257 if (link(FileName.c_str(),OriginalPath) != 0)
258 {
259 // Panic! Restore the symlink
260 symlink(OldLink,OriginalPath);
261 return _error->Errno("link",_("*** Failed to link %s to %s"),
262 FileName.c_str(),
263 OriginalPath);
264 }
265 }
266 }
267 }
268
269 DeLinkBytes += St.st_size;
270 if (DeLinkBytes/1024 >= DeLinkLimit)
271 ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());
272 }
273
274 FileName = OriginalPath;
275 }
276
277 return true;
278}
279 /*}}}*/
280// FTWScanner::SetExts - Set extensions to support /*{{{*/
281// ---------------------------------------------------------------------
282/* */
283bool FTWScanner::SetExts(string Vals)
284{
285 delete [] TmpExt;
286 TmpExt = new char[Vals.length()+1];
287 strcpy(TmpExt,Vals.c_str());
288 return TokSplitString(' ',TmpExt,(char **)Ext,sizeof(Ext)/sizeof(Ext[0]));
289}
290 /*}}}*/
291
292// PackagesWriter::PackagesWriter - Constructor /*{{{*/
293// ---------------------------------------------------------------------
294/* */
295PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides) :
296 Db(DB),Stats(Db.Stats)
297{
298 Output = stdout;
299 Ext[0] = ".deb";
300 Ext[1] = 0;
301 DeLinkLimit = 0;
302
303 // Process the command line options
304 DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
305 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
306 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
307
308 if (Db.Loaded() == false)
309 DoContents = false;
310
311 // Read the override file
312 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
313 return;
314 else
315 NoOverride = true;
316
317 if (ExtOverrides.empty() == false)
318 Over.ReadExtraOverride(ExtOverrides);
319
320 _error->DumpErrors();
321}
322 /*}}}*/
323// PackagesWriter::DoPackage - Process a single package /*{{{*/
324// ---------------------------------------------------------------------
325/* This method takes a package and gets its control information and
326 MD5 then writes out a control record with the proper fields rewritten
327 and the path/size/hash appended. */
328bool PackagesWriter::DoPackage(string FileName)
329{
330 // Open the archive
331 FileFd F(FileName,FileFd::ReadOnly);
332 if (_error->PendingError() == true)
333 return false;
334
335 // Stat the file for later
336 struct stat St;
337 if (fstat(F.Fd(),&St) != 0)
338 return _error->Errno("fstat",_("Failed to stat %s"),FileName.c_str());
339
340 // Pull all the data we need form the DB
341 string MD5Res;
342 if (Db.SetFile(FileName,St,&F) == false ||
343 Db.LoadControl() == false ||
344 (DoContents == true && Db.LoadContents(true) == false) ||
345 (DoMD5 == true && Db.GetMD5(MD5Res,false) == false))
346 return false;
347
348 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,St) == false)
349 return false;
350
351 // Lookup the overide information
352 pkgTagSection &Tags = Db.Control.Section;
353 string Package = Tags.FindS("Package");
354 Override::Item Tmp;
355 Override::Item *OverItem = Over.GetItem(Package);
356
357 if (Package.empty() == true)
358 return _error->Error(_("Archive had no package field"));
359
360 // If we need to do any rewriting of the header do it now..
361 if (OverItem == 0)
362 {
363 if (NoOverride == false)
364 {
365 NewLine(1);
366 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
367 }
368
369 OverItem = &Tmp;
370 Tmp.FieldOverride["Section"] = Tags.FindS("Section");
371 Tmp.Priority = Tags.FindS("Priority");
372 }
373
374 char Size[40];
375 sprintf(Size,"%lu",St.st_size);
376
377 // Strip the DirStrip prefix from the FileName and add the PathPrefix
378 string NewFileName;
379 if (DirStrip.empty() == false &&
380 FileName.length() > DirStrip.length() &&
381 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
382 DirStrip.begin(),DirStrip.end()) == 0)
383 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
384 else
385 NewFileName = FileName;
386 if (PathPrefix.empty() == false)
387 NewFileName = flCombine(PathPrefix,NewFileName);
388
389 // This lists all the changes to the fields we are going to make.
390 // (7 hardcoded + maintainer + suggests + end marker)
391 TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1];
392
393 unsigned int End = 0;
394 SetTFRewriteData(Changes[End++], "Size", Size);
395 SetTFRewriteData(Changes[End++], "MD5sum", MD5Res.c_str());
396 SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
397 SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
398 SetTFRewriteData(Changes[End++], "Status", 0);
399 SetTFRewriteData(Changes[End++], "Optional", 0);
400
401 // Rewrite the maintainer field if necessary
402 bool MaintFailed;
403 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
404 if (MaintFailed == true)
405 {
406 if (NoOverride == false)
407 {
408 NewLine(1);
409 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
410 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
411 }
412 }
413
414 if (NewMaint.empty() == false)
415 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
416
417 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
418 dpkg-scanpackages does.. Well sort of. dpkg-scanpackages just does renaming
419 but dpkg does this append bit. So we do the append bit, at least that way the
420 status file and package file will remain similar. There are other transforms
421 but optional is the only legacy one still in use for some lazy reason. */
422 string OptionalStr = Tags.FindS("Optional");
423 if (OptionalStr.empty() == false)
424 {
425 if (Tags.FindS("Suggests").empty() == false)
426 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
427 SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
428 }
429
430 for (map<string,string>::iterator I = OverItem->FieldOverride.begin();
431 I != OverItem->FieldOverride.end(); I++)
432 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
433
434 SetTFRewriteData(Changes[End++], 0, 0);
435
436 // Rewrite and store the fields.
437 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
438 return false;
439 fprintf(Output,"\n");
440
441 return Db.Finish();
442}
443 /*}}}*/
444
445// SourcesWriter::SourcesWriter - Constructor /*{{{*/
446// ---------------------------------------------------------------------
447/* */
448SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
449 string ExtOverrides)
450{
451 Output = stdout;
452 Ext[0] = ".dsc";
453 Ext[1] = 0;
454 DeLinkLimit = 0;
455 Buffer = 0;
456 BufSize = 0;
457
458 // Process the command line options
459 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
460
461 // Read the override file
462 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
463 return;
464 else
465 NoOverride = true;
466
467 if (ExtOverrides.empty() == false)
468 SOver.ReadExtraOverride(ExtOverrides);
469
470 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
471 SOver.ReadOverride(SOverrides,true);
472}
473 /*}}}*/
474// SourcesWriter::DoPackage - Process a single package /*{{{*/
475// ---------------------------------------------------------------------
476/* */
477bool SourcesWriter::DoPackage(string FileName)
478{
479 // Open the archive
480 FileFd F(FileName,FileFd::ReadOnly);
481 if (_error->PendingError() == true)
482 return false;
483
484 // Stat the file for later
485 struct stat St;
486 if (fstat(F.Fd(),&St) != 0)
487 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
488
489 if (St.st_size > 128*1024)
490 return _error->Error("DSC file '%s' is too large!",FileName.c_str());
491
492 if (BufSize < (unsigned)St.st_size+1)
493 {
494 BufSize = St.st_size+1;
495 Buffer = (char *)realloc(Buffer,St.st_size+1);
496 }
497
498 if (F.Read(Buffer,St.st_size) == false)
499 return false;
500
501 // Hash the file
502 char *Start = Buffer;
503 char *BlkEnd = Buffer + St.st_size;
504 MD5Summation MD5;
505 MD5.Add((unsigned char *)Start,BlkEnd - Start);
506
507 // Add an extra \n to the end, just in case
508 *BlkEnd++ = '\n';
509
510 /* Remove the PGP trailer. Some .dsc's have this without a blank line
511 before */
512 const char *Key = "-----BEGIN PGP SIGNATURE-----";
513 for (char *MsgEnd = Start; MsgEnd < BlkEnd - strlen(Key) -1; MsgEnd++)
514 {
515 if (*MsgEnd == '\n' && strncmp(MsgEnd+1,Key,strlen(Key)) == 0)
516 {
517 MsgEnd[1] = '\n';
518 break;
519 }
520 }
521
522 /* Read records until we locate the Source record. This neatly skips the
523 GPG header (which is RFC822 formed) without any trouble. */
524 pkgTagSection Tags;
525 do
526 {
527 unsigned Pos;
528 if (Tags.Scan(Start,BlkEnd - Start) == false)
529 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
530 if (Tags.Find("Source",Pos) == true)
531 break;
532 Start += Tags.size();
533 }
534 while (1);
535 Tags.Trim();
536
537 // Lookup the overide information, finding first the best priority.
538 string BestPrio;
539 char Buffer[1000];
540 string Bins = Tags.FindS("Binary");
541 Override::Item *OverItem = 0;
542 if (Bins.empty() == false && Bins.length() < sizeof(Buffer))
543 {
544 strcpy(Buffer,Bins.c_str());
545
546 // Ignore too-long errors.
547 char *BinList[400];
548 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
549
550 // Look at all the binaries
551 unsigned char BestPrioV = pkgCache::State::Extra;
552 for (unsigned I = 0; BinList[I] != 0; I++)
553 {
554 Override::Item *Itm = BOver.GetItem(BinList[I]);
555 if (Itm == 0)
556 continue;
557 if (OverItem == 0)
558 OverItem = Itm;
559
560 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
561 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
562 {
563 BestPrioV = NewPrioV;
564 BestPrio = Itm->Priority;
565 }
566 }
567 }
568
569 // If we need to do any rewriting of the header do it now..
570 Override::Item Tmp;
571 if (OverItem == 0)
572 {
573 if (NoOverride == false)
574 {
575 NewLine(1);
576 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
577 }
578
579 OverItem = &Tmp;
580 }
581
582 Override::Item *SOverItem = SOver.GetItem(Tags.FindS("Source"));
583 if (SOverItem == 0)
584 {
585 SOverItem = BOver.GetItem(Tags.FindS("Source"));
586 if (SOverItem == 0)
587 SOverItem = OverItem;
588 }
589
590 // Add the dsc to the files hash list
591 char Files[1000];
592 snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
593 string(MD5.Result()).c_str(),St.st_size,
594 flNotDir(FileName).c_str(),
595 Tags.FindS("Files").c_str());
596
597 // Strip the DirStrip prefix from the FileName and add the PathPrefix
598 string NewFileName;
599 if (DirStrip.empty() == false &&
600 FileName.length() > DirStrip.length() &&
601 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
602 NewFileName = string(OriginalPath + DirStrip.length());
603 else
604 NewFileName = OriginalPath;
605 if (PathPrefix.empty() == false)
606 NewFileName = flCombine(PathPrefix,NewFileName);
607
608 string Directory = flNotFile(OriginalPath);
609 string Package = Tags.FindS("Source");
610
611 // Perform the delinking operation over all of the files
612 string ParseJnk;
613 const char *C = Files;
614 for (;isspace(*C); C++);
615 while (*C != 0)
616 {
617 // Parse each of the elements
618 if (ParseQuoteWord(C,ParseJnk) == false ||
619 ParseQuoteWord(C,ParseJnk) == false ||
620 ParseQuoteWord(C,ParseJnk) == false)
621 return _error->Error("Error parsing file record");
622
623 char Jnk[2];
624 string OriginalPath = Directory + ParseJnk;
625 if (RealPath != 0 && readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
626 realpath(OriginalPath.c_str(),RealPath) != 0)
627 {
628 string RP = RealPath;
629 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St) == false)
630 return false;
631 }
632 }
633
634 Directory = flNotFile(NewFileName);
635 if (Directory.length() > 2)
636 Directory.erase(Directory.end()-1);
637
638 // This lists all the changes to the fields we are going to make.
639 // (5 hardcoded + maintainer + end marker)
640 TFRewriteData Changes[5+1+SOverItem->FieldOverride.size()+1];
641
642 unsigned int End = 0;
643 SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
644 SetTFRewriteData(Changes[End++],"Files",Files);
645 if (Directory != "./")
646 SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
647 SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
648 SetTFRewriteData(Changes[End++],"Status",0);
649
650 // Rewrite the maintainer field if necessary
651 bool MaintFailed;
652 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
653 if (MaintFailed == true)
654 {
655 if (NoOverride == false)
656 {
657 NewLine(1);
658 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
659 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
660 }
661 }
662 if (NewMaint.empty() == false)
663 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
664
665 for (map<string,string>::iterator I = SOverItem->FieldOverride.begin();
666 I != SOverItem->FieldOverride.end(); I++)
667 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
668
669 SetTFRewriteData(Changes[End++], 0, 0);
670
671 // Rewrite and store the fields.
672 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
673 return false;
674 fprintf(Output,"\n");
675
676 Stats.Packages++;
677
678 return true;
679}
680 /*}}}*/
681
682// ContentsWriter::ContentsWriter - Constructor /*{{{*/
683// ---------------------------------------------------------------------
684/* */
685ContentsWriter::ContentsWriter(string DB) :
686 Db(DB), Stats(Db.Stats)
687
688{
689 Ext[0] = ".deb";
690 Ext[1] = 0;
691 Output = stdout;
692}
693 /*}}}*/
694// ContentsWriter::DoPackage - Process a single package /*{{{*/
695// ---------------------------------------------------------------------
696/* If Package is the empty string the control record will be parsed to
697 determine what the package name is. */
698bool ContentsWriter::DoPackage(string FileName,string Package)
699{
700 // Open the archive
701 FileFd F(FileName,FileFd::ReadOnly);
702 if (_error->PendingError() == true)
703 return false;
704
705 // Stat the file for later
706 struct stat St;
707 if (fstat(F.Fd(),&St) != 0)
708 return _error->Errno("fstat","Failed too stat %s",FileName.c_str());
709
710 // Ready the DB
711 if (Db.SetFile(FileName,St,&F) == false ||
712 Db.LoadContents(false) == false)
713 return false;
714
715 // Parse the package name
716 if (Package.empty() == true)
717 {
718 if (Db.LoadControl() == false)
719 return false;
720 Package = Db.Control.Section.FindS("Package");
721 }
722
723 Db.Contents.Add(Gen,Package);
724
725 return Db.Finish();
726}
727 /*}}}*/
728// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
729// ---------------------------------------------------------------------
730/* */
731bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
732{
733 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
734 if (_error->PendingError() == true)
735 return false;
736
737 // Open the package file
738 int CompFd = -1;
739 int Proc = -1;
740 if (Pkgs.OpenOld(CompFd,Proc) == false)
741 return false;
742
743 // No auto-close FD
744 FileFd Fd(CompFd,false);
745 pkgTagFile Tags(&Fd);
746 if (_error->PendingError() == true)
747 {
748 Pkgs.CloseOld(CompFd,Proc);
749 return false;
750 }
751
752 // Parse.
753 pkgTagSection Section;
754 while (Tags.Step(Section) == true)
755 {
756 string File = flCombine(Prefix,Section.FindS("FileName"));
757 string Package = Section.FindS("Section");
758 if (Package.empty() == false && Package.end()[-1] != '/')
759 {
760 Package += '/';
761 Package += Section.FindS("Package");
762 }
763 else
764 Package += Section.FindS("Package");
765
766 DoPackage(File,Package);
767 if (_error->empty() == false)
768 {
769 _error->Error("Errors apply to file '%s'",File.c_str());
770 _error->DumpErrors();
771 }
772 }
773
774 // Tidy the compressor
775 if (Pkgs.CloseOld(CompFd,Proc) == false)
776 return false;
777
778 return true;
779}
780 /*}}}*/