]> git.saurik.com Git - apt.git/blame - ftparchive/writer.cc
* apt-pkg/deb/dpkgpm.cc:
[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}
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
DK
301PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string const &ExtOverrides,
302 string const &aArch) :
0b41e0e7 303 Db(DB),Stats(Db.Stats), Arch(aArch)
b2e465d6
AL
304{
305 Output = stdout;
d6689735 306 SetExts(".deb .udeb .foo .bar .baz");
98953965 307 AddPattern("*.deb");
b2e465d6
AL
308 DeLinkLimit = 0;
309
310 // Process the command line options
311 DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
cde41ae8
MV
312 DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
313 DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
ff574e76 314 DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
b2e465d6
AL
315 DoContents = _config->FindB("APT::FTPArchive::Contents",true);
316 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
9c24493f 317 LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
b2e465d6
AL
318
319 if (Db.Loaded() == false)
320 DoContents = false;
64177f17 321
b2e465d6
AL
322 // Read the override file
323 if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
324 return;
325 else
326 NoOverride = true;
64177f17
AL
327
328 if (ExtOverrides.empty() == false)
329 Over.ReadExtraOverride(ExtOverrides);
330
b2e465d6
AL
331 _error->DumpErrors();
332}
98953965
AL
333 /*}}}*/
334// FTWScanner::SetExts - Set extensions to support /*{{{*/
335// ---------------------------------------------------------------------
336/* */
9209ec47 337bool FTWScanner::SetExts(string const &Vals)
98953965
AL
338{
339 ClearPatterns();
340 string::size_type Start = 0;
d6689735 341 while (Start <= Vals.length()-1)
98953965 342 {
d6689735
AL
343 string::size_type Space = Vals.find(' ',Start);
344 string::size_type Length;
345 if (Space == string::npos)
98953965 346 {
d6689735 347 Length = Vals.length()-Start;
98953965 348 }
d6689735
AL
349 else
350 {
351 Length = Space-Start;
352 }
353 AddPattern(string("*") + Vals.substr(Start, Length));
354 Start += Length + 1;
98953965
AL
355 }
356
357 return true;
358}
359
b2e465d6
AL
360 /*}}}*/
361// PackagesWriter::DoPackage - Process a single package /*{{{*/
362// ---------------------------------------------------------------------
363/* This method takes a package and gets its control information and
cde41ae8
MV
364 MD5, SHA1 and SHA256 then writes out a control record with the proper fields
365 rewritten and the path/size/hash appended. */
b2e465d6
AL
366bool PackagesWriter::DoPackage(string FileName)
367{
b2e465d6 368 // Pull all the data we need form the DB
ff574e76 369 if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256, DoAlwaysStat)
cde41ae8
MV
370 == false)
371 {
b2e465d6 372 return false;
cde41ae8 373 }
b2e465d6 374
cde41ae8
MV
375 off_t FileSize = Db.GetFileSize();
376 if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
b2e465d6
AL
377 return false;
378
379 // Lookup the overide information
380 pkgTagSection &Tags = Db.Control.Section;
381 string Package = Tags.FindS("Package");
0b41e0e7
MV
382 string Architecture;
383 // if we generate a Packages file for a given arch, we use it to
384 // look for overrides. if we run in "simple" mode without the
385 // "Architecures" variable in the config we use the architecure value
386 // from the deb file
387 if(Arch != "")
388 Architecture = Arch;
389 else
390 Architecture = Tags.FindS("Architecture");
391 auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
b2e465d6
AL
392
393 if (Package.empty() == true)
dc738e7a 394 return _error->Error(_("Archive had no package field"));
0b41e0e7 395
b2e465d6 396 // If we need to do any rewriting of the header do it now..
0b41e0e7 397 if (OverItem.get() == 0)
b2e465d6
AL
398 {
399 if (NoOverride == false)
400 {
401 NewLine(1);
dc738e7a 402 ioprintf(c1out, _(" %s has no override entry\n"), Package.c_str());
b2e465d6
AL
403 }
404
0b41e0e7
MV
405 OverItem = auto_ptr<Override::Item>(new Override::Item);
406 OverItem->FieldOverride["Section"] = Tags.FindS("Section");
407 OverItem->Priority = Tags.FindS("Priority");
b2e465d6
AL
408 }
409
410 char Size[40];
cde41ae8 411 sprintf(Size,"%lu", (unsigned long) FileSize);
b2e465d6
AL
412
413 // Strip the DirStrip prefix from the FileName and add the PathPrefix
414 string NewFileName;
415 if (DirStrip.empty() == false &&
416 FileName.length() > DirStrip.length() &&
417 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
418 DirStrip.begin(),DirStrip.end()) == 0)
419 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
420 else
421 NewFileName = FileName;
422 if (PathPrefix.empty() == false)
423 NewFileName = flCombine(PathPrefix,NewFileName);
9c24493f
DK
424
425 /* Configuration says we don't want to include the long Description
426 in the package file - instead we want to ship a separated file */
427 string desc;
428 if (LongDescription == false) {
429 desc = Tags.FindS("Description").append("\n");
430 OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
431 }
432
b2e465d6 433 // This lists all the changes to the fields we are going to make.
64177f17 434 // (7 hardcoded + maintainer + suggests + end marker)
9c24493f 435 TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1+1];
64177f17 436
b2e465d6 437 unsigned int End = 0;
64177f17 438 SetTFRewriteData(Changes[End++], "Size", Size);
cde41ae8
MV
439 SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
440 SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
441 SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
64177f17
AL
442 SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
443 SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
444 SetTFRewriteData(Changes[End++], "Status", 0);
445 SetTFRewriteData(Changes[End++], "Optional", 0);
446
9c24493f
DK
447 string DescriptionMd5;
448 if (LongDescription == false) {
449 MD5Summation descmd5;
450 descmd5.Add(desc.c_str());
451 DescriptionMd5 = descmd5.Result().Value();
452 SetTFRewriteData(Changes[End++], "Description-md5", DescriptionMd5.c_str());
453 }
454
b2e465d6
AL
455 // Rewrite the maintainer field if necessary
456 bool MaintFailed;
457 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
458 if (MaintFailed == true)
459 {
460 if (NoOverride == false)
461 {
462 NewLine(1);
dc738e7a
AL
463 ioprintf(c1out, _(" %s maintainer is %s not %s\n"),
464 Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
b2e465d6
AL
465 }
466 }
467
468 if (NewMaint.empty() == false)
64177f17 469 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
b2e465d6
AL
470
471 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
c6474fb6 472 dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
b2e465d6
AL
473 but dpkg does this append bit. So we do the append bit, at least that way the
474 status file and package file will remain similar. There are other transforms
475 but optional is the only legacy one still in use for some lazy reason. */
476 string OptionalStr = Tags.FindS("Optional");
477 if (OptionalStr.empty() == false)
478 {
479 if (Tags.FindS("Suggests").empty() == false)
480 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
64177f17 481 SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
b2e465d6 482 }
64177f17 483
9209ec47 484 for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
64177f17
AL
485 I != OverItem->FieldOverride.end(); I++)
486 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
487
488 SetTFRewriteData(Changes[End++], 0, 0);
489
b2e465d6
AL
490 // Rewrite and store the fields.
491 if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
492 return false;
493 fprintf(Output,"\n");
494
495 return Db.Finish();
496}
497 /*}}}*/
498
499// SourcesWriter::SourcesWriter - Constructor /*{{{*/
500// ---------------------------------------------------------------------
501/* */
9209ec47
DK
502SourcesWriter::SourcesWriter(string const &BOverrides,string const &SOverrides,
503 string const &ExtOverrides)
b2e465d6
AL
504{
505 Output = stdout;
98953965 506 AddPattern("*.dsc");
b2e465d6
AL
507 DeLinkLimit = 0;
508 Buffer = 0;
509 BufSize = 0;
510
511 // Process the command line options
512 NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
513
514 // Read the override file
515 if (BOverrides.empty() == false && BOver.ReadOverride(BOverrides) == false)
516 return;
517 else
518 NoOverride = true;
64177f17 519
cde41ae8
MV
520 // WTF?? The logic above: if we can't read binary overrides, don't even try
521 // reading source overrides. if we can read binary overrides, then say there
522 // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
523
64177f17
AL
524 if (ExtOverrides.empty() == false)
525 SOver.ReadExtraOverride(ExtOverrides);
b2e465d6 526
64177f17
AL
527 if (SOverrides.empty() == false && FileExists(SOverrides) == true)
528 SOver.ReadOverride(SOverrides,true);
b2e465d6
AL
529}
530 /*}}}*/
531// SourcesWriter::DoPackage - Process a single package /*{{{*/
532// ---------------------------------------------------------------------
533/* */
534bool SourcesWriter::DoPackage(string FileName)
535{
536 // Open the archive
537 FileFd F(FileName,FileFd::ReadOnly);
538 if (_error->PendingError() == true)
539 return false;
540
541 // Stat the file for later
542 struct stat St;
543 if (fstat(F.Fd(),&St) != 0)
544 return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
545
546 if (St.st_size > 128*1024)
547 return _error->Error("DSC file '%s' is too large!",FileName.c_str());
548
549 if (BufSize < (unsigned)St.st_size+1)
550 {
551 BufSize = St.st_size+1;
552 Buffer = (char *)realloc(Buffer,St.st_size+1);
553 }
554
555 if (F.Read(Buffer,St.st_size) == false)
556 return false;
557
558 // Hash the file
559 char *Start = Buffer;
560 char *BlkEnd = Buffer + St.st_size;
561 MD5Summation MD5;
562 MD5.Add((unsigned char *)Start,BlkEnd - Start);
f99da908
DK
563
564 SHA1Summation SHA1;
565 SHA256Summation SHA256;
566 SHA1.Add((unsigned char *)Start,BlkEnd - Start);
567 SHA256.Add((unsigned char *)Start,BlkEnd - Start);
568
b2e465d6
AL
569 // Add an extra \n to the end, just in case
570 *BlkEnd++ = '\n';
571
572 /* Remove the PGP trailer. Some .dsc's have this without a blank line
573 before */
574 const char *Key = "-----BEGIN PGP SIGNATURE-----";
575 for (char *MsgEnd = Start; MsgEnd < BlkEnd - strlen(Key) -1; MsgEnd++)
576 {
577 if (*MsgEnd == '\n' && strncmp(MsgEnd+1,Key,strlen(Key)) == 0)
578 {
579 MsgEnd[1] = '\n';
580 break;
581 }
582 }
583
584 /* Read records until we locate the Source record. This neatly skips the
585 GPG header (which is RFC822 formed) without any trouble. */
586 pkgTagSection Tags;
587 do
588 {
589 unsigned Pos;
590 if (Tags.Scan(Start,BlkEnd - Start) == false)
591 return _error->Error("Could not find a record in the DSC '%s'",FileName.c_str());
592 if (Tags.Find("Source",Pos) == true)
593 break;
594 Start += Tags.size();
595 }
596 while (1);
597 Tags.Trim();
598
599 // Lookup the overide information, finding first the best priority.
600 string BestPrio;
b2e465d6 601 string Bins = Tags.FindS("Binary");
5200ec6f 602 char Buffer[Bins.length() + 1];
0b41e0e7 603 auto_ptr<Override::Item> OverItem(0);
5200ec6f 604 if (Bins.empty() == false)
b2e465d6
AL
605 {
606 strcpy(Buffer,Bins.c_str());
607
608 // Ignore too-long errors.
609 char *BinList[400];
610 TokSplitString(',',Buffer,BinList,sizeof(BinList)/sizeof(BinList[0]));
611
612 // Look at all the binaries
613 unsigned char BestPrioV = pkgCache::State::Extra;
614 for (unsigned I = 0; BinList[I] != 0; I++)
615 {
0b41e0e7
MV
616 auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
617 if (Itm.get() == 0)
b2e465d6 618 continue;
b2e465d6
AL
619
620 unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
621 if (NewPrioV < BestPrioV || BestPrio.empty() == true)
622 {
623 BestPrioV = NewPrioV;
624 BestPrio = Itm->Priority;
625 }
7524e348
MV
626
627 if (OverItem.get() == 0)
628 OverItem = Itm;
b2e465d6
AL
629 }
630 }
631
632 // If we need to do any rewriting of the header do it now..
0b41e0e7 633 if (OverItem.get() == 0)
b2e465d6
AL
634 {
635 if (NoOverride == false)
636 {
637 NewLine(1);
dc738e7a 638 ioprintf(c1out, _(" %s has no override entry\n"), Tags.FindS("Source").c_str());
b2e465d6
AL
639 }
640
0b41e0e7 641 OverItem = auto_ptr<Override::Item>(new Override::Item);
b2e465d6
AL
642 }
643
0b41e0e7 644 auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
cde41ae8 645 // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
0b41e0e7 646 if (SOverItem.get() == 0)
b2e465d6 647 {
cde41ae8 648 ioprintf(c1out, _(" %s has no source override entry\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
649 SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
650 if (SOverItem.get() == 0)
651 {
cde41ae8 652 ioprintf(c1out, _(" %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
0b41e0e7
MV
653 SOverItem = auto_ptr<Override::Item>(new Override::Item);
654 *SOverItem = *OverItem;
655 }
b2e465d6
AL
656 }
657
658 // Add the dsc to the files hash list
f99da908 659 string const strippedName = flNotDir(FileName);
b2e465d6
AL
660 char Files[1000];
661 snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
662 string(MD5.Result()).c_str(),St.st_size,
f99da908 663 strippedName.c_str(),
b2e465d6 664 Tags.FindS("Files").c_str());
f99da908
DK
665
666 char ChecksumsSha1[1000];
667 snprintf(ChecksumsSha1,sizeof(ChecksumsSha1),"\n %s %lu %s\n %s",
668 string(SHA1.Result()).c_str(),St.st_size,
669 strippedName.c_str(),
670 Tags.FindS("Checksums-Sha1").c_str());
671
672 char ChecksumsSha256[1000];
673 snprintf(ChecksumsSha256,sizeof(ChecksumsSha256),"\n %s %lu %s\n %s",
674 string(SHA256.Result()).c_str(),St.st_size,
675 strippedName.c_str(),
676 Tags.FindS("Checksums-Sha256").c_str());
677
b2e465d6
AL
678 // Strip the DirStrip prefix from the FileName and add the PathPrefix
679 string NewFileName;
680 if (DirStrip.empty() == false &&
681 FileName.length() > DirStrip.length() &&
8c58f506 682 stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
b2e465d6
AL
683 NewFileName = string(OriginalPath + DirStrip.length());
684 else
685 NewFileName = OriginalPath;
686 if (PathPrefix.empty() == false)
687 NewFileName = flCombine(PathPrefix,NewFileName);
171c45bc 688
b2e465d6
AL
689 string Directory = flNotFile(OriginalPath);
690 string Package = Tags.FindS("Source");
171c45bc 691
b2e465d6
AL
692 // Perform the delinking operation over all of the files
693 string ParseJnk;
694 const char *C = Files;
ab3846c0 695 char *RealPath = NULL;
b2e465d6
AL
696 for (;isspace(*C); C++);
697 while (*C != 0)
698 {
699 // Parse each of the elements
700 if (ParseQuoteWord(C,ParseJnk) == false ||
701 ParseQuoteWord(C,ParseJnk) == false ||
702 ParseQuoteWord(C,ParseJnk) == false)
703 return _error->Error("Error parsing file record");
704
705 char Jnk[2];
706 string OriginalPath = Directory + ParseJnk;
ab3846c0
MV
707 if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
708 (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
b2e465d6
AL
709 {
710 string RP = RealPath;
ab3846c0 711 free(RealPath);
cde41ae8 712 if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
b2e465d6
AL
713 return false;
714 }
715 }
716
717 Directory = flNotFile(NewFileName);
718 if (Directory.length() > 2)
719 Directory.erase(Directory.end()-1);
171c45bc 720
b2e465d6 721 // This lists all the changes to the fields we are going to make.
f99da908
DK
722 // (5 hardcoded + checksums + maintainer + end marker)
723 TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
64177f17 724
b2e465d6 725 unsigned int End = 0;
64177f17
AL
726 SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
727 SetTFRewriteData(Changes[End++],"Files",Files);
f99da908
DK
728 SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1);
729 SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256);
171c45bc
AL
730 if (Directory != "./")
731 SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
64177f17
AL
732 SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
733 SetTFRewriteData(Changes[End++],"Status",0);
b2e465d6
AL
734
735 // Rewrite the maintainer field if necessary
736 bool MaintFailed;
737 string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
738 if (MaintFailed == true)
739 {
740 if (NoOverride == false)
741 {
742 NewLine(1);
dc738e7a
AL
743 ioprintf(c1out, _(" %s maintainer is %s not %s\n"), Package.c_str(),
744 Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
b2e465d6
AL
745 }
746 }
747 if (NewMaint.empty() == false)
64177f17
AL
748 SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
749
9209ec47 750 for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin();
64177f17
AL
751 I != SOverItem->FieldOverride.end(); I++)
752 SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
753
754 SetTFRewriteData(Changes[End++], 0, 0);
b2e465d6
AL
755
756 // Rewrite and store the fields.
757 if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
758 return false;
759 fprintf(Output,"\n");
760
761 Stats.Packages++;
762
763 return true;
764}
765 /*}}}*/
766
767// ContentsWriter::ContentsWriter - Constructor /*{{{*/
768// ---------------------------------------------------------------------
769/* */
9209ec47 770ContentsWriter::ContentsWriter(string const &DB) :
b2e465d6
AL
771 Db(DB), Stats(Db.Stats)
772
773{
98953965 774 AddPattern("*.deb");
b2e465d6
AL
775 Output = stdout;
776}
777 /*}}}*/
778// ContentsWriter::DoPackage - Process a single package /*{{{*/
779// ---------------------------------------------------------------------
780/* If Package is the empty string the control record will be parsed to
781 determine what the package name is. */
9209ec47 782bool ContentsWriter::DoPackage(string FileName, string Package)
b2e465d6 783{
ff574e76 784 if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false, false))
cde41ae8 785 {
b2e465d6 786 return false;
cde41ae8 787 }
b2e465d6
AL
788
789 // Parse the package name
790 if (Package.empty() == true)
791 {
b2e465d6
AL
792 Package = Db.Control.Section.FindS("Package");
793 }
794
795 Db.Contents.Add(Gen,Package);
796
797 return Db.Finish();
798}
799 /*}}}*/
800// ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
801// ---------------------------------------------------------------------
802/* */
9209ec47 803bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
b2e465d6
AL
804{
805 MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
806 if (_error->PendingError() == true)
807 return false;
808
809 // Open the package file
810 int CompFd = -1;
b3d44315 811 pid_t Proc = -1;
b2e465d6
AL
812 if (Pkgs.OpenOld(CompFd,Proc) == false)
813 return false;
814
815 // No auto-close FD
816 FileFd Fd(CompFd,false);
817 pkgTagFile Tags(&Fd);
818 if (_error->PendingError() == true)
819 {
820 Pkgs.CloseOld(CompFd,Proc);
821 return false;
822 }
823
824 // Parse.
825 pkgTagSection Section;
826 while (Tags.Step(Section) == true)
827 {
828 string File = flCombine(Prefix,Section.FindS("FileName"));
829 string Package = Section.FindS("Section");
830 if (Package.empty() == false && Package.end()[-1] != '/')
831 {
832 Package += '/';
833 Package += Section.FindS("Package");
834 }
835 else
836 Package += Section.FindS("Package");
837
838 DoPackage(File,Package);
839 if (_error->empty() == false)
840 {
841 _error->Error("Errors apply to file '%s'",File.c_str());
842 _error->DumpErrors();
843 }
844 }
845
846 // Tidy the compressor
847 if (Pkgs.CloseOld(CompFd,Proc) == false)
848 return false;
849
850 return true;
851}
98953965 852
b2e465d6 853 /*}}}*/
98953965
AL
854
855// ReleaseWriter::ReleaseWriter - Constructor /*{{{*/
856// ---------------------------------------------------------------------
857/* */
9209ec47 858ReleaseWriter::ReleaseWriter(string const &DB)
98953965
AL
859{
860 AddPattern("Packages");
861 AddPattern("Packages.gz");
187492a6 862 AddPattern("Packages.bz2");
e85b4cd5 863 AddPattern("Packages.lzma");
187492a6
AL
864 AddPattern("Sources");
865 AddPattern("Sources.gz");
866 AddPattern("Sources.bz2");
e85b4cd5 867 AddPattern("Sources.lzma");
187492a6
AL
868 AddPattern("Release");
869 AddPattern("md5sum.txt");
870
98953965 871 Output = stdout;
9209ec47 872 time_t const now = time(NULL);
98953965
AL
873 char datestr[128];
874 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
875 gmtime(&now)) == 0)
876 {
877 datestr[0] = '\0';
878 }
879
880 map<string,string> Fields;
881 Fields["Origin"] = "";
882 Fields["Label"] = "";
883 Fields["Suite"] = "";
884 Fields["Version"] = "";
885 Fields["Codename"] = "";
886 Fields["Date"] = datestr;
887 Fields["Architectures"] = "";
888 Fields["Components"] = "";
889 Fields["Description"] = "";
890
891 for(map<string,string>::const_iterator I = Fields.begin();
892 I != Fields.end();
893 ++I)
894 {
895 string Config = string("APT::FTPArchive::Release::") + (*I).first;
896 string Value = _config->Find(Config, (*I).second.c_str());
897 if (Value == "")
898 continue;
899
900 fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
901 }
98953965
AL
902}
903 /*}}}*/
904// ReleaseWriter::DoPackage - Process a single package /*{{{*/
905// ---------------------------------------------------------------------
906bool ReleaseWriter::DoPackage(string FileName)
907{
908 // Strip the DirStrip prefix from the FileName and add the PathPrefix
909 string NewFileName;
910 if (DirStrip.empty() == false &&
911 FileName.length() > DirStrip.length() &&
912 stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
913 DirStrip.begin(),DirStrip.end()) == 0)
c0eb6bc6 914 {
98953965 915 NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
c0eb6bc6 916 while (NewFileName[0] == '/')
9202409d 917 NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
c0eb6bc6 918 }
98953965
AL
919 else
920 NewFileName = FileName;
c0eb6bc6 921
98953965
AL
922 if (PathPrefix.empty() == false)
923 NewFileName = flCombine(PathPrefix,NewFileName);
924
925 FileFd fd(FileName, FileFd::ReadOnly);
926
927 if (!fd.IsOpen())
928 {
929 return false;
930 }
931
c0eb6bc6 932 CheckSums[NewFileName].size = fd.Size();
f7291f62 933
98953965
AL
934 MD5Summation MD5;
935 MD5.AddFD(fd.Fd(), fd.Size());
c0eb6bc6 936 CheckSums[NewFileName].MD5 = MD5.Result();
98953965 937
f7291f62
AL
938 fd.Seek(0);
939 SHA1Summation SHA1;
940 SHA1.AddFD(fd.Fd(), fd.Size());
c0eb6bc6 941 CheckSums[NewFileName].SHA1 = SHA1.Result();
98953965 942
cde41ae8
MV
943 fd.Seek(0);
944 SHA256Summation SHA256;
945 SHA256.AddFD(fd.Fd(), fd.Size());
946 CheckSums[NewFileName].SHA256 = SHA256.Result();
947
98953965
AL
948 fd.Close();
949
950 return true;
951}
f7291f62
AL
952
953 /*}}}*/
954// ReleaseWriter::Finish - Output the checksums /*{{{*/
955// ---------------------------------------------------------------------
956void ReleaseWriter::Finish()
957{
958 fprintf(Output, "MD5Sum:\n");
9209ec47 959 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
f7291f62
AL
960 I != CheckSums.end();
961 ++I)
962 {
963 fprintf(Output, " %s %16ld %s\n",
964 (*I).second.MD5.c_str(),
965 (*I).second.size,
966 (*I).first.c_str());
967 }
968
969 fprintf(Output, "SHA1:\n");
9209ec47 970 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
f7291f62
AL
971 I != CheckSums.end();
972 ++I)
973 {
974 fprintf(Output, " %s %16ld %s\n",
975 (*I).second.SHA1.c_str(),
976 (*I).second.size,
977 (*I).first.c_str());
978 }
cde41ae8
MV
979
980 fprintf(Output, "SHA256:\n");
9209ec47 981 for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
cde41ae8
MV
982 I != CheckSums.end();
983 ++I)
984 {
985 fprintf(Output, " %s %16ld %s\n",
986 (*I).second.SHA256.c_str(),
987 (*I).second.size,
988 (*I).first.c_str());
989 }
f7291f62
AL
990}
991