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