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