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