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