]>
git.saurik.com Git - apt.git/blob - ftparchive/writer.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.4 2001/06/26 02:50:27 jgg Exp $
4 /* ######################################################################
8 The file writer classes. These write various types of output, sources,
11 ##################################################################### */
13 // Include Files /*{{{*/
15 #pragma implementation "writer.h"
20 #include <apt-pkg/strutl.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/md5.h>
24 #include <apt-pkg/deblistparser.h>
26 #include <sys/types.h>
32 #include "apt-ftparchive.h"
33 #include "multicompress.h"
37 FTWScanner
*FTWScanner::Owner
;
39 // SetTFRewriteData - Helper for setting rewrite lists /*{{{*/
40 // ---------------------------------------------------------------------
42 inline void SetTFRewriteData(struct TFRewriteData
&tfrd
,
45 const char *newtag
= 0)
48 tfrd
.Rewrite
= rewrite
;
53 // FTWScanner::FTWScanner - Constructor /*{{{*/
54 // ---------------------------------------------------------------------
56 FTWScanner::FTWScanner()
59 NoLinkAct
= !_config
->FindB("APT::FTPArchive::DeLinkAct",true);
63 long PMax
= pathconf(".",_PC_PATH_MAX
);
65 RealPath
= new char[PMax
];
68 // FTWScanner::Scanner - FTW Scanner /*{{{*/
69 // ---------------------------------------------------------------------
70 /* This is the FTW scanner, it processes each directory element in the
72 int FTWScanner::Scanner(const char *File
,const struct stat
*sb
,int Flag
)
77 c1out
<< "W: Unable to read directory " << File
<< endl
;
82 c1out
<< "W: Unable to stat " << File
<< endl
;
87 // See if it is a .deb
92 for (; Owner
->Ext
[CurExt
] != 0; CurExt
++)
93 if (strcmp(File
+strlen(File
)-strlen(Owner
->Ext
[CurExt
]),
94 Owner
->Ext
[CurExt
]) == 0)
96 if (Owner
->Ext
[CurExt
] == 0)
99 /* Process it. If the file is a link then resolve it into an absolute
100 name.. This works best if the directory components the scanner are
101 given are not links themselves. */
103 Owner
->OriginalPath
= File
;
104 if (Owner
->RealPath
!= 0 && readlink(File
,Jnk
,sizeof(Jnk
)) != -1 &&
105 realpath(File
,Owner
->RealPath
) != 0)
106 Owner
->DoPackage(Owner
->RealPath
);
108 Owner
->DoPackage(File
);
110 if (_error
->empty() == false)
112 // Print any errors or warnings found
114 bool SeenPath
= false;
115 while (_error
->empty() == false)
119 bool Type
= _error
->PopMessage(Err
);
121 c1out
<< "E: " << Err
<< endl
;
123 c1out
<< "W: " << Err
<< endl
;
125 if (Err
.find(File
) != string::npos
)
129 if (SeenPath
== false)
130 cerr
<< "E: Errors apply to file '" << File
<< "'" << endl
;
137 // FTWScanner::RecursiveScan - Just scan a directory tree /*{{{*/
138 // ---------------------------------------------------------------------
140 bool FTWScanner::RecursiveScan(string Dir
)
142 /* If noprefix is set then jam the scan root in, so we don't generate
143 link followed paths out of control */
144 if (InternalPrefix
.empty() == true)
146 if (realpath(Dir
.c_str(),RealPath
) == 0)
147 return _error
->Errno("realpath","Failed to resolve %s",Dir
.c_str());
148 InternalPrefix
= RealPath
;
151 // Do recursive directory searching
153 int Res
= ftw(Dir
.c_str(),Scanner
,30);
155 // Error treewalking?
158 if (_error
->PendingError() == false)
159 _error
->Errno("ftw","Tree walking failed");
166 // FTWScanner::LoadFileList - Load the file list from a file /*{{{*/
167 // ---------------------------------------------------------------------
168 /* This is an alternative to using FTW to locate files, it reads the list
169 of files from another file. */
170 bool FTWScanner::LoadFileList(string Dir
,string File
)
172 /* If noprefix is set then jam the scan root in, so we don't generate
173 link followed paths out of control */
174 if (InternalPrefix
.empty() == true)
176 if (realpath(Dir
.c_str(),RealPath
) == 0)
177 return _error
->Errno("realpath","Failed to resolve %s",Dir
.c_str());
178 InternalPrefix
= RealPath
;
182 FILE *List
= fopen(File
.c_str(),"r");
184 return _error
->Errno("fopen","Failed to open %s",File
.c_str());
186 /* We are a tad tricky here.. We prefix the buffer with the directory
187 name, that way if we need a full path with just use line.. Sneaky and
191 if (Dir
.empty() == true || Dir
.end()[-1] != '/')
192 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s/",Dir
.c_str());
194 FileStart
= Line
+ snprintf(Line
,sizeof(Line
),"%s",Dir
.c_str());
195 while (fgets(FileStart
,sizeof(Line
) - (FileStart
- Line
),List
) != 0)
197 char *FileName
= _strstrip(FileStart
);
198 if (FileName
[0] == 0)
201 if (FileName
[0] != '/')
203 if (FileName
!= FileStart
)
204 memmove(FileStart
,FileName
,strlen(FileStart
));
210 if (stat(FileName
,&St
) != 0)
213 if (Scanner(FileName
,&St
,Flag
) != 0)
221 // FTWScanner::Delink - Delink symlinks /*{{{*/
222 // ---------------------------------------------------------------------
224 bool FTWScanner::Delink(string
&FileName
,const char *OriginalPath
,
225 unsigned long &DeLinkBytes
,
228 // See if this isn't an internaly prefix'd file name.
229 if (InternalPrefix
.empty() == false &&
230 InternalPrefix
.length() < FileName
.length() &&
231 stringcmp(FileName
.begin(),FileName
.begin() + InternalPrefix
.length(),
232 InternalPrefix
.begin(),InternalPrefix
.end()) != 0)
234 if (DeLinkLimit
!= 0 && DeLinkBytes
/1024 < DeLinkLimit
)
236 // Tidy up the display
237 if (DeLinkBytes
== 0)
241 c1out
<< " DeLink " << (OriginalPath
+ InternalPrefix
.length())
242 << " [" << SizeToStr(St
.st_size
) << "B]" << endl
<< flush
;
244 if (NoLinkAct
== false)
247 if (readlink(OriginalPath
,OldLink
,sizeof(OldLink
)) == -1)
248 _error
->Errno("readlink","Failed to readlink %s",OriginalPath
);
251 if (unlink(OriginalPath
) != 0)
252 _error
->Errno("unlink","Failed to unlink %s",OriginalPath
);
255 if (link(FileName
.c_str(),OriginalPath
) != 0)
257 // Panic! Restore the symlink
258 symlink(OldLink
,OriginalPath
);
259 return _error
->Errno("link","*** Failed to link %s to %s",
267 DeLinkBytes
+= St
.st_size
;
268 if (DeLinkBytes
/1024 >= DeLinkLimit
)
269 c1out
<< " DeLink limit of " << SizeToStr(DeLinkBytes
) << "B hit." << endl
;
272 FileName
= OriginalPath
;
278 // FTWScanner::SetExts - Set extensions to support /*{{{*/
279 // ---------------------------------------------------------------------
281 bool FTWScanner::SetExts(string Vals
)
284 TmpExt
= new char[Vals
.length()+1];
285 strcpy(TmpExt
,Vals
.c_str());
286 return TokSplitString(' ',TmpExt
,(char **)Ext
,sizeof(Ext
)/sizeof(Ext
[0]));
290 // PackagesWriter::PackagesWriter - Constructor /*{{{*/
291 // ---------------------------------------------------------------------
293 PackagesWriter::PackagesWriter(string DB
,string Overrides
,string ExtOverrides
) :
294 Db(DB
),Stats(Db
.Stats
)
301 // Process the command line options
302 DoMD5
= _config
->FindB("APT::FTPArchive::MD5",true);
303 DoContents
= _config
->FindB("APT::FTPArchive::Contents",true);
304 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
306 if (Db
.Loaded() == false)
309 // Read the override file
310 if (Overrides
.empty() == false && Over
.ReadOverride(Overrides
) == false)
315 if (ExtOverrides
.empty() == false)
316 Over
.ReadExtraOverride(ExtOverrides
);
318 _error
->DumpErrors();
321 // PackagesWriter::DoPackage - Process a single package /*{{{*/
322 // ---------------------------------------------------------------------
323 /* This method takes a package and gets its control information and
324 MD5 then writes out a control record with the proper fields rewritten
325 and the path/size/hash appended. */
326 bool PackagesWriter::DoPackage(string FileName
)
329 FileFd
F(FileName
,FileFd::ReadOnly
);
330 if (_error
->PendingError() == true)
333 // Stat the file for later
335 if (fstat(F
.Fd(),&St
) != 0)
336 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
338 // Pull all the data we need form the DB
340 if (Db
.SetFile(FileName
,St
,&F
) == false ||
341 Db
.LoadControl() == false ||
342 (DoContents
== true && Db
.LoadContents(true) == false) ||
343 (DoMD5
== true && Db
.GetMD5(MD5Res
,false) == false))
346 if (Delink(FileName
,OriginalPath
,Stats
.DeLinkBytes
,St
) == false)
349 // Lookup the overide information
350 pkgTagSection
&Tags
= Db
.Control
.Section
;
351 string Package
= Tags
.FindS("Package");
353 Override::Item
*OverItem
= Over
.GetItem(Package
);
355 if (Package
.empty() == true)
356 return _error
->Error("Archive had no package field");
358 // If we need to do any rewriting of the header do it now..
361 if (NoOverride
== false)
364 c1out
<< " " << Package
<< " has no override entry" << endl
;
368 Tmp
.FieldOverride
["Section"] = Tags
.FindS("Section");
369 Tmp
.Priority
= Tags
.FindS("Priority");
373 sprintf(Size
,"%lu",St
.st_size
);
375 // Strip the DirStrip prefix from the FileName and add the PathPrefix
377 if (DirStrip
.empty() == false &&
378 FileName
.length() > DirStrip
.length() &&
379 stringcmp(FileName
.begin(),FileName
.begin() + DirStrip
.length(),
380 DirStrip
.begin(),DirStrip
.end()) == 0)
381 NewFileName
= string(FileName
.begin() + DirStrip
.length(),FileName
.end());
383 NewFileName
= FileName
;
384 if (PathPrefix
.empty() == false)
385 NewFileName
= flCombine(PathPrefix
,NewFileName
);
387 // This lists all the changes to the fields we are going to make.
388 // (7 hardcoded + maintainer + suggests + end marker)
389 TFRewriteData Changes
[6+2+OverItem
->FieldOverride
.size()+1];
391 unsigned int End
= 0;
392 SetTFRewriteData(Changes
[End
++], "Size", Size
);
393 SetTFRewriteData(Changes
[End
++], "MD5sum", MD5Res
.c_str());
394 SetTFRewriteData(Changes
[End
++], "Filename", NewFileName
.c_str());
395 SetTFRewriteData(Changes
[End
++], "Priority", OverItem
->Priority
.c_str());
396 SetTFRewriteData(Changes
[End
++], "Status", 0);
397 SetTFRewriteData(Changes
[End
++], "Optional", 0);
399 // Rewrite the maintainer field if necessary
401 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
402 if (MaintFailed
== true)
404 if (NoOverride
== false)
407 c1out
<< " " << Package
<< " maintainer is " <<
408 Tags
.FindS("Maintainer") << " not " <<
409 OverItem
->OldMaint
<< endl
;
413 if (NewMaint
.empty() == false)
414 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
416 /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
417 dpkg-scanpackages does.. Well sort of. dpkg-scanpackages just does renaming
418 but dpkg does this append bit. So we do the append bit, at least that way the
419 status file and package file will remain similar. There are other transforms
420 but optional is the only legacy one still in use for some lazy reason. */
421 string OptionalStr
= Tags
.FindS("Optional");
422 if (OptionalStr
.empty() == false)
424 if (Tags
.FindS("Suggests").empty() == false)
425 OptionalStr
= Tags
.FindS("Suggests") + ", " + OptionalStr
;
426 SetTFRewriteData(Changes
[End
++], "Suggests", OptionalStr
.c_str());
429 for (map
<string
,string
>::iterator I
= OverItem
->FieldOverride
.begin();
430 I
!= OverItem
->FieldOverride
.end(); I
++)
431 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
433 SetTFRewriteData(Changes
[End
++], 0, 0);
435 // Rewrite and store the fields.
436 if (TFRewrite(Output
,Tags
,TFRewritePackageOrder
,Changes
) == false)
438 fprintf(Output
,"\n");
444 // SourcesWriter::SourcesWriter - Constructor /*{{{*/
445 // ---------------------------------------------------------------------
447 SourcesWriter::SourcesWriter(string BOverrides
,string SOverrides
,
457 // Process the command line options
458 NoOverride
= _config
->FindB("APT::FTPArchive::NoOverrideMsg",false);
460 // Read the override file
461 if (BOverrides
.empty() == false && BOver
.ReadOverride(BOverrides
) == false)
466 if (ExtOverrides
.empty() == false)
467 SOver
.ReadExtraOverride(ExtOverrides
);
469 if (SOverrides
.empty() == false && FileExists(SOverrides
) == true)
470 SOver
.ReadOverride(SOverrides
,true);
473 // SourcesWriter::DoPackage - Process a single package /*{{{*/
474 // ---------------------------------------------------------------------
476 bool SourcesWriter::DoPackage(string FileName
)
479 FileFd
F(FileName
,FileFd::ReadOnly
);
480 if (_error
->PendingError() == true)
483 // Stat the file for later
485 if (fstat(F
.Fd(),&St
) != 0)
486 return _error
->Errno("fstat","Failed to stat %s",FileName
.c_str());
488 if (St
.st_size
> 128*1024)
489 return _error
->Error("DSC file '%s' is too large!",FileName
.c_str());
491 if (BufSize
< (unsigned)St
.st_size
+1)
493 BufSize
= St
.st_size
+1;
494 Buffer
= (char *)realloc(Buffer
,St
.st_size
+1);
497 if (F
.Read(Buffer
,St
.st_size
) == false)
501 char *Start
= Buffer
;
502 char *BlkEnd
= Buffer
+ St
.st_size
;
504 MD5
.Add((unsigned char *)Start
,BlkEnd
- Start
);
506 // Add an extra \n to the end, just in case
509 /* Remove the PGP trailer. Some .dsc's have this without a blank line
511 const char *Key
= "-----BEGIN PGP SIGNATURE-----";
512 for (char *MsgEnd
= Start
; MsgEnd
< BlkEnd
- strlen(Key
) -1; MsgEnd
++)
514 if (*MsgEnd
== '\n' && strncmp(MsgEnd
+1,Key
,strlen(Key
)) == 0)
521 /* Read records until we locate the Source record. This neatly skips the
522 GPG header (which is RFC822 formed) without any trouble. */
527 if (Tags
.Scan(Start
,BlkEnd
- Start
) == false)
528 return _error
->Error("Could not find a record in the DSC '%s'",FileName
.c_str());
529 if (Tags
.Find("Source",Pos
) == true)
531 Start
+= Tags
.size();
536 // Lookup the overide information, finding first the best priority.
539 string Bins
= Tags
.FindS("Binary");
540 Override::Item
*OverItem
= 0;
541 if (Bins
.empty() == false && Bins
.length() < sizeof(Buffer
))
543 strcpy(Buffer
,Bins
.c_str());
545 // Ignore too-long errors.
547 TokSplitString(',',Buffer
,BinList
,sizeof(BinList
)/sizeof(BinList
[0]));
549 // Look at all the binaries
550 unsigned char BestPrioV
= pkgCache::State::Extra
;
551 for (unsigned I
= 0; BinList
[I
] != 0; I
++)
553 Override::Item
*Itm
= BOver
.GetItem(BinList
[I
]);
559 unsigned char NewPrioV
= debListParser::GetPrio(Itm
->Priority
);
560 if (NewPrioV
< BestPrioV
|| BestPrio
.empty() == true)
562 BestPrioV
= NewPrioV
;
563 BestPrio
= Itm
->Priority
;
568 // If we need to do any rewriting of the header do it now..
572 if (NoOverride
== false)
575 c1out
<< " " << Tags
.FindS("Source") << " has no override entry" << endl
;
581 Override::Item
*SOverItem
= SOver
.GetItem(Tags
.FindS("Source"));
584 SOverItem
= BOver
.GetItem(Tags
.FindS("Source"));
586 SOverItem
= OverItem
;
589 // Add the dsc to the files hash list
591 snprintf(Files
,sizeof(Files
),"\n %s %lu %s\n %s",
592 string(MD5
.Result()).c_str(),St
.st_size
,
593 flNotDir(FileName
).c_str(),
594 Tags
.FindS("Files").c_str());
596 // Strip the DirStrip prefix from the FileName and add the PathPrefix
598 if (DirStrip
.empty() == false &&
599 FileName
.length() > DirStrip
.length() &&
600 stringcmp(DirStrip
,OriginalPath
,OriginalPath
+ DirStrip
.length()) == 0)
601 NewFileName
= string(OriginalPath
+ DirStrip
.length());
603 NewFileName
= OriginalPath
;
604 if (PathPrefix
.empty() == false)
605 NewFileName
= flCombine(PathPrefix
,NewFileName
);
607 string Directory
= flNotFile(OriginalPath
);
608 string Package
= Tags
.FindS("Source");
610 // Perform the delinking operation over all of the files
612 const char *C
= Files
;
613 for (;isspace(*C
); C
++);
616 // Parse each of the elements
617 if (ParseQuoteWord(C
,ParseJnk
) == false ||
618 ParseQuoteWord(C
,ParseJnk
) == false ||
619 ParseQuoteWord(C
,ParseJnk
) == false)
620 return _error
->Error("Error parsing file record");
623 string OriginalPath
= Directory
+ ParseJnk
;
624 if (RealPath
!= 0 && readlink(OriginalPath
.c_str(),Jnk
,sizeof(Jnk
)) != -1 &&
625 realpath(OriginalPath
.c_str(),RealPath
) != 0)
627 string RP
= RealPath
;
628 if (Delink(RP
,OriginalPath
.c_str(),Stats
.DeLinkBytes
,St
) == false)
633 Directory
= flNotFile(NewFileName
);
634 if (Directory
.length() > 2)
635 Directory
.erase(Directory
.end()-1);
637 // This lists all the changes to the fields we are going to make.
638 // (5 hardcoded + maintainer + end marker)
639 TFRewriteData Changes
[5+1+SOverItem
->FieldOverride
.size()+1];
641 unsigned int End
= 0;
642 SetTFRewriteData(Changes
[End
++],"Source",Package
.c_str(),"Package");
643 SetTFRewriteData(Changes
[End
++],"Files",Files
);
644 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
645 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
646 SetTFRewriteData(Changes
[End
++],"Status",0);
648 // Rewrite the maintainer field if necessary
650 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
651 if (MaintFailed
== true)
653 if (NoOverride
== false)
656 c1out
<< " " << Package
<< " maintainer is " <<
657 Tags
.FindS("Maintainer") << " not " <<
658 OverItem
->OldMaint
<< endl
;
661 if (NewMaint
.empty() == false)
662 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
664 for (map
<string
,string
>::iterator I
= SOverItem
->FieldOverride
.begin();
665 I
!= SOverItem
->FieldOverride
.end(); I
++)
666 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
668 SetTFRewriteData(Changes
[End
++], 0, 0);
670 // Rewrite and store the fields.
671 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
673 fprintf(Output
,"\n");
681 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
682 // ---------------------------------------------------------------------
684 ContentsWriter::ContentsWriter(string DB
) :
685 Db(DB
), Stats(Db
.Stats
)
693 // ContentsWriter::DoPackage - Process a single package /*{{{*/
694 // ---------------------------------------------------------------------
695 /* If Package is the empty string the control record will be parsed to
696 determine what the package name is. */
697 bool ContentsWriter::DoPackage(string FileName
,string Package
)
700 FileFd
F(FileName
,FileFd::ReadOnly
);
701 if (_error
->PendingError() == true)
704 // Stat the file for later
706 if (fstat(F
.Fd(),&St
) != 0)
707 return _error
->Errno("fstat","Failed too stat %s",FileName
.c_str());
710 if (Db
.SetFile(FileName
,St
,&F
) == false ||
711 Db
.LoadContents(false) == false)
714 // Parse the package name
715 if (Package
.empty() == true)
717 if (Db
.LoadControl() == false)
719 Package
= Db
.Control
.Section
.FindS("Package");
722 Db
.Contents
.Add(Gen
,Package
);
727 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
728 // ---------------------------------------------------------------------
730 bool ContentsWriter::ReadFromPkgs(string PkgFile
,string PkgCompress
)
732 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
733 if (_error
->PendingError() == true)
736 // Open the package file
739 if (Pkgs
.OpenOld(CompFd
,Proc
) == false)
743 FileFd
Fd(CompFd
,false);
744 pkgTagFile
Tags(&Fd
);
745 if (_error
->PendingError() == true)
747 Pkgs
.CloseOld(CompFd
,Proc
);
752 pkgTagSection Section
;
753 while (Tags
.Step(Section
) == true)
755 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
756 string Package
= Section
.FindS("Section");
757 if (Package
.empty() == false && Package
.end()[-1] != '/')
760 Package
+= Section
.FindS("Package");
763 Package
+= Section
.FindS("Package");
765 DoPackage(File
,Package
);
766 if (_error
->empty() == false)
768 _error
->Error("Errors apply to file '%s'",File
.c_str());
769 _error
->DumpErrors();
773 // Tidy the compressor
774 if (Pkgs
.CloseOld(CompFd
,Proc
) == false)