]>
git.saurik.com Git - apt.git/blob - ftparchive/writer.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: writer.cc,v 1.6 2002/11/11 04:27:51 doogie 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 cerr
<< "E: " << Err
<< endl
;
123 cerr
<< "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 if (Directory
!= "./")
645 SetTFRewriteData(Changes
[End
++],"Directory",Directory
.c_str());
646 SetTFRewriteData(Changes
[End
++],"Priority",BestPrio
.c_str());
647 SetTFRewriteData(Changes
[End
++],"Status",0);
649 // Rewrite the maintainer field if necessary
651 string NewMaint
= OverItem
->SwapMaint(Tags
.FindS("Maintainer"),MaintFailed
);
652 if (MaintFailed
== true)
654 if (NoOverride
== false)
657 c1out
<< " " << Package
<< " maintainer is " <<
658 Tags
.FindS("Maintainer") << " not " <<
659 OverItem
->OldMaint
<< endl
;
662 if (NewMaint
.empty() == false)
663 SetTFRewriteData(Changes
[End
++], "Maintainer", NewMaint
.c_str());
665 for (map
<string
,string
>::iterator I
= SOverItem
->FieldOverride
.begin();
666 I
!= SOverItem
->FieldOverride
.end(); I
++)
667 SetTFRewriteData(Changes
[End
++],I
->first
.c_str(),I
->second
.c_str());
669 SetTFRewriteData(Changes
[End
++], 0, 0);
671 // Rewrite and store the fields.
672 if (TFRewrite(Output
,Tags
,TFRewriteSourceOrder
,Changes
) == false)
674 fprintf(Output
,"\n");
682 // ContentsWriter::ContentsWriter - Constructor /*{{{*/
683 // ---------------------------------------------------------------------
685 ContentsWriter::ContentsWriter(string DB
) :
686 Db(DB
), Stats(Db
.Stats
)
694 // ContentsWriter::DoPackage - Process a single package /*{{{*/
695 // ---------------------------------------------------------------------
696 /* If Package is the empty string the control record will be parsed to
697 determine what the package name is. */
698 bool ContentsWriter::DoPackage(string FileName
,string Package
)
701 FileFd
F(FileName
,FileFd::ReadOnly
);
702 if (_error
->PendingError() == true)
705 // Stat the file for later
707 if (fstat(F
.Fd(),&St
) != 0)
708 return _error
->Errno("fstat","Failed too stat %s",FileName
.c_str());
711 if (Db
.SetFile(FileName
,St
,&F
) == false ||
712 Db
.LoadContents(false) == false)
715 // Parse the package name
716 if (Package
.empty() == true)
718 if (Db
.LoadControl() == false)
720 Package
= Db
.Control
.Section
.FindS("Package");
723 Db
.Contents
.Add(Gen
,Package
);
728 // ContentsWriter::ReadFromPkgs - Read from a packages file /*{{{*/
729 // ---------------------------------------------------------------------
731 bool ContentsWriter::ReadFromPkgs(string PkgFile
,string PkgCompress
)
733 MultiCompress
Pkgs(PkgFile
,PkgCompress
,0,false);
734 if (_error
->PendingError() == true)
737 // Open the package file
740 if (Pkgs
.OpenOld(CompFd
,Proc
) == false)
744 FileFd
Fd(CompFd
,false);
745 pkgTagFile
Tags(&Fd
);
746 if (_error
->PendingError() == true)
748 Pkgs
.CloseOld(CompFd
,Proc
);
753 pkgTagSection Section
;
754 while (Tags
.Step(Section
) == true)
756 string File
= flCombine(Prefix
,Section
.FindS("FileName"));
757 string Package
= Section
.FindS("Section");
758 if (Package
.empty() == false && Package
.end()[-1] != '/')
761 Package
+= Section
.FindS("Package");
764 Package
+= Section
.FindS("Package");
766 DoPackage(File
,Package
);
767 if (_error
->empty() == false)
769 _error
->Error("Errors apply to file '%s'",File
.c_str());
770 _error
->DumpErrors();
774 // Tidy the compressor
775 if (Pkgs
.CloseOld(CompFd
,Proc
) == false)