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