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