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