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