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