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