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