]>
git.saurik.com Git - apt.git/blob - apt-pkg/indexcopy.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: indexcopy.cc,v 1.10 2002/03/26 07:38:58 jgg Exp $
4 /* ######################################################################
6 Index Copying - Aid for copying and verifying the index files
8 This class helps apt-cache reconstruct a damaged index files.
10 ##################################################################### */
12 // Include Files /*{{{*/
13 #include "indexcopy.h"
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/progress.h>
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/tagfile.h>
21 #include <apt-pkg/indexrecords.h>
22 #include <apt-pkg/md5.h>
23 #include <apt-pkg/cdrom.h>
37 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
38 // ---------------------------------------------------------------------
40 bool IndexCopy::CopyPackages(string CDROM
,string Name
,vector
<string
> &List
,
43 OpProgress
*Progress
= NULL
;
48 Progress
= log
->GetOpProgress();
50 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
51 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
53 // Prepare the progress indicator
54 unsigned long TotalSize
= 0;
55 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
58 if (stat(string(*I
+ GetFileName()).c_str(),&Buf
) != 0 &&
59 stat(string(*I
+ GetFileName() + ".gz").c_str(),&Buf
) != 0)
60 return _error
->Errno("stat","Stat failed for %s",
61 string(*I
+ GetFileName()).c_str());
62 TotalSize
+= Buf
.st_size
;
65 unsigned long CurrentSize
= 0;
66 unsigned int NotFound
= 0;
67 unsigned int WrongSize
= 0;
68 unsigned int Packages
= 0;
69 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
71 string OrigPath
= string(*I
,CDROM
.length());
72 unsigned long FileSize
= 0;
74 // Open the package file
76 if (FileExists(*I
+ GetFileName()) == true)
78 Pkg
.Open(*I
+ GetFileName(),FileFd::ReadOnly
);
79 FileSize
= Pkg
.Size();
83 FileFd
From(*I
+ GetFileName() + ".gz",FileFd::ReadOnly
);
84 if (_error
->PendingError() == true)
86 FileSize
= From
.Size();
89 FILE *tmp
= tmpfile();
91 return _error
->Errno("tmpfile","Unable to create a tmp file");
92 Pkg
.Fd(dup(fileno(tmp
)));
96 pid_t Process
= fork();
98 return _error
->Errno("fork","Couldn't fork gzip");
103 dup2(From
.Fd(),STDIN_FILENO
);
104 dup2(Pkg
.Fd(),STDOUT_FILENO
);
105 SetCloseExec(STDIN_FILENO
,false);
106 SetCloseExec(STDOUT_FILENO
,false);
109 string Tmp
= _config
->Find("Dir::bin::gzip","gzip");
110 Args
[0] = Tmp
.c_str();
113 execvp(Args
[0],(char **)Args
);
117 // Wait for gzip to finish
118 if (ExecWait(Process
,_config
->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
119 return _error
->Error("gzip failed, perhaps the disk is full.");
123 pkgTagFile
Parser(&Pkg
);
124 if (_error
->PendingError() == true)
127 // Open the output file
129 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",Name
.c_str(),
130 (*I
).c_str() + CDROM
.length(),GetFileName());
131 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
132 TargetF
+= URItoFileName(S
);
133 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
134 TargetF
= "/dev/null";
135 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
136 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
137 if (_error
->PendingError() == true)
140 return _error
->Errno("fdopen","Failed to reopen fd");
142 // Setup the progress meter
144 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
145 string("Reading ") + Type() + " Indexes");
149 Progress
->SubProgress(Pkg
.Size());
150 pkgTagSection Section
;
151 this->Section
= &Section
;
153 unsigned long Hits
= 0;
154 unsigned long Chop
= 0;
155 while (Parser
.Step(Section
) == true)
158 Progress
->Progress(Parser
.Offset());
161 if (GetFile(File
,Size
) == false)
168 File
= OrigPath
+ ChopDirs(File
,Chop
);
170 // See if the file exists
171 bool Mangled
= false;
172 if (NoStat
== false || Hits
< 10)
174 // Attempt to fix broken structure
177 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
178 ReconstructChop(Chop
,*I
,File
) == false)
181 clog
<< "Missed: " << File
<< endl
;
186 File
= OrigPath
+ ChopDirs(File
,Chop
);
191 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0 ||
194 // Attempt to fix busted symlink support for one instance
195 string OrigFile
= File
;
196 string::size_type Start
= File
.find("binary-");
197 string::size_type End
= File
.find("/",Start
+3);
198 if (Start
!= string::npos
&& End
!= string::npos
)
200 File
.replace(Start
,End
-Start
,"binary-all");
204 if (Mangled
== false ||
205 stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
208 clog
<< "Missed(2): " << OrigFile
<< endl
;
215 if ((unsigned)Buf
.st_size
!= Size
)
218 clog
<< "Wrong Size: " << File
<< endl
;
227 if (RewriteEntry(TargetFl
,File
) == false)
236 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
238 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
240 // Move out of the partial directory
242 string FinalF
= _config
->FindDir("Dir::State::lists");
243 FinalF
+= URItoFileName(S
);
244 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
245 return _error
->Errno("rename","Failed to rename");
248 /* Mangle the source to be in the proper notation with
249 prefix dist [component] */
250 *I
= string(*I
,Prefix
.length());
251 ConvertToSourceList(CDROM
,*I
);
252 *I
= Prefix
+ ' ' + *I
;
254 CurrentSize
+= FileSize
;
262 if(NotFound
== 0 && WrongSize
== 0)
263 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
264 else if (NotFound
!= 0 && WrongSize
== 0)
265 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
267 else if (NotFound
== 0 && WrongSize
!= 0)
268 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
269 Packages
, WrongSize
);
270 if (NotFound
!= 0 && WrongSize
!= 0)
271 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
275 _error
->Warning("No valid records were found.");
277 if (NotFound
+ WrongSize
> 10)
278 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");
284 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
285 // ---------------------------------------------------------------------
287 string
IndexCopy::ChopDirs(string Path
,unsigned int Depth
)
289 string::size_type I
= 0;
292 I
= Path
.find('/',I
+1);
295 while (I
!= string::npos
&& Depth
!= 0);
297 if (I
== string::npos
)
300 return string(Path
,I
+1);
303 // IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
304 // ---------------------------------------------------------------------
305 /* This prepends dir components from the path to the package files to
306 the path to the deb until it is found */
307 bool IndexCopy::ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
310 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
311 unsigned int Depth
= 1;
312 string MyPrefix
= Prefix
;
316 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
319 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
320 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
334 // IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
335 // ---------------------------------------------------------------------
336 /* This removes path components from the filename and prepends the location
337 of the package files until a file is found */
338 bool IndexCopy::ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
340 // Attempt to reconstruct the filename
341 unsigned long Depth
= 0;
345 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
347 File
= ChopDirs(File
,1);
349 if (File
.empty() == false)
362 // IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
363 // ---------------------------------------------------------------------
364 /* We look for things in dists/ notation and convert them to
365 <dist> <component> form otherwise it is left alone. This also strips
368 This implements a regex sort of like:
369 (.*)/dists/([^/]*)/(.*)/binary-*
371 | |-------- Distribution
372 |------------------- Path
374 It was deciced to use only a single word for dist (rather than say
375 unstable/non-us) to increase the chance that each CD gets a single
376 line in sources.list.
378 void IndexCopy::ConvertToSourceList(string CD
,string
&Path
)
381 snprintf(S
,sizeof(S
),"binary-%s",_config
->Find("Apt::Architecture").c_str());
383 // Strip the cdrom base path
384 Path
= string(Path
,CD
.length());
385 if (Path
.empty() == true)
388 // Too short to be a dists/ type
389 if (Path
.length() < strlen("dists/"))
393 if (stringcmp(Path
.c_str(),Path
.c_str()+strlen("dists/"),"dists/") != 0)
397 string::size_type Slash
= strlen("dists/");
398 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
399 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
401 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
403 // Isolate the component
405 for (unsigned I
= 0; I
!= 10; I
++)
407 Slash
= Path
.find('/',Slash
+1);
408 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
410 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
412 // Verify the trailing binary- bit
413 string::size_type BinSlash
= Path
.find('/',Slash
+ 1);
414 if (Slash
== string::npos
)
416 string Binary
= string(Path
,Slash
+1,BinSlash
- Slash
-1);
418 if (Binary
!= S
&& Binary
!= "source")
421 Path
= Dist
+ ' ' + Comp
;
426 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
427 // ---------------------------------------------------------------------
429 bool IndexCopy::GrabFirst(string Path
,string
&To
,unsigned int Depth
)
431 string::size_type I
= 0;
434 I
= Path
.find('/',I
+1);
437 while (I
!= string::npos
&& Depth
!= 0);
439 if (I
== string::npos
)
442 To
= string(Path
,0,I
+1);
446 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
447 // ---------------------------------------------------------------------
449 bool PackageCopy::GetFile(string
&File
,unsigned long &Size
)
451 File
= Section
->FindS("Filename");
452 Size
= Section
->FindI("Size");
453 if (File
.empty() || Size
== 0)
454 return _error
->Error("Cannot find filename or size tag");
458 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
459 // ---------------------------------------------------------------------
461 bool PackageCopy::RewriteEntry(FILE *Target
,string File
)
463 TFRewriteData Changes
[] = {{"Filename",File
.c_str()},
466 if (TFRewrite(Target
,*Section
,TFRewritePackageOrder
,Changes
) == false)
472 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
473 // ---------------------------------------------------------------------
475 bool SourceCopy::GetFile(string
&File
,unsigned long &Size
)
477 string Files
= Section
->FindS("Files");
478 if (Files
.empty() == true)
481 // Stash the / terminated directory prefix
482 string Base
= Section
->FindS("Directory");
483 if (Base
.empty() == false && Base
[Base
.length()-1] != '/')
486 // Read the first file triplet
487 const char *C
= Files
.c_str();
491 // Parse each of the elements
492 if (ParseQuoteWord(C
,MD5Hash
) == false ||
493 ParseQuoteWord(C
,sSize
) == false ||
494 ParseQuoteWord(C
,File
) == false)
495 return _error
->Error("Error parsing file record");
497 // Parse the size and append the directory
498 Size
= atoi(sSize
.c_str());
503 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
504 // ---------------------------------------------------------------------
506 bool SourceCopy::RewriteEntry(FILE *Target
,string File
)
508 string
Dir(File
,0,File
.rfind('/'));
509 TFRewriteData Changes
[] = {{"Directory",Dir
.c_str()},
512 if (TFRewrite(Target
,*Section
,TFRewriteSourceOrder
,Changes
) == false)
518 // SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
519 // ---------------------------------------------------------------------
521 bool SigVerify::Verify(string prefix
, string file
, indexRecords
*MetaIndex
)
523 const indexRecords::checkSum
*Record
= MetaIndex
->Lookup(file
);
525 // we skip non-existing files in the verifcation to support a cdrom
526 // with no Packages file (just a Package.gz), see LP: #255545
527 // (non-existing files are not considered a error)
528 if(!FileExists(prefix
+file
))
530 _error
->Warning(_("Skipping nonexistent file %s"), string(prefix
+file
).c_str());
536 _error
->Warning(_("Can't find authentication record for: %s"), file
.c_str());
540 if (!Record
->Hash
.VerifyFile(prefix
+file
))
542 _error
->Warning(_("Hash mismatch for: %s"),file
.c_str());
546 if(_config
->FindB("Debug::aptcdrom",false))
548 cout
<< "File: " << prefix
+file
<< endl
;
549 cout
<< "Expected Hash " << Record
->Hash
.toStr() << endl
;
555 bool SigVerify::CopyMetaIndex(string CDROM
, string CDName
, /*{{{*/
556 string prefix
, string file
)
559 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",CDName
.c_str(),
560 (prefix
).c_str() + CDROM
.length(),file
.c_str());
561 string TargetF
= _config
->FindDir("Dir::State::lists");
562 TargetF
+= URItoFileName(S
);
566 Target
.Open(TargetF
,FileFd::WriteEmpty
);
567 Rel
.Open(prefix
+ file
,FileFd::ReadOnly
);
568 if (_error
->PendingError() == true)
570 if (CopyFile(Rel
,Target
) == false)
576 bool SigVerify::CopyAndVerify(string CDROM
,string Name
,vector
<string
> &SigList
, /*{{{*/
577 vector
<string
> PkgList
,vector
<string
> SrcList
)
579 if (SigList
.size() == 0)
582 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
584 // Read all Release files
585 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
588 cout
<< "Signature verify for: " << *I
<< endl
;
590 indexRecords
*MetaIndex
= new indexRecords
;
593 string
const releasegpg
= *I
+"Release.gpg";
594 string
const release
= *I
+"Release";
596 // a Release.gpg without a Release should never happen
597 if(FileExists(release
) == false)
603 pid_t pid
= ExecFork();
605 _error
->Error("Fork failed");
609 string
const gpgvpath
= _config
->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
610 std::vector
<const char*> Args
= GetGPGVCommandLine();
611 Args
.push_back(releasegpg
.c_str());
612 Args
.push_back(release
.c_str());
613 Args
.push_back(NULL
);
614 execvp(gpgvpath
.c_str(), (char**) &Args
[0]);
616 if(!ExecWait(pid
, "gpgv")) {
617 _error
->Warning("Signature verification failed for: %s",
619 // something went wrong, don't copy the Release.gpg
620 // FIXME: delete any existing gpg file?
624 // Open the Release file and add it to the MetaIndex
625 if(!MetaIndex
->Load(release
))
627 _error
->Error("%s",MetaIndex
->ErrorText
.c_str());
631 // go over the Indexfiles and see if they verify
632 // if so, remove them from our copy of the lists
633 vector
<string
> keys
= MetaIndex
->MetaKeys();
634 for (vector
<string
>::iterator I
= keys
.begin(); I
!= keys
.end(); I
++)
636 if(!Verify(prefix
,*I
, MetaIndex
)) {
637 // something went wrong, don't copy the Release.gpg
638 // FIXME: delete any existing gpg file?
644 // we need a fresh one for the Release.gpg
647 // everything was fine, copy the Release and Release.gpg file
648 CopyMetaIndex(CDROM
, Name
, prefix
, "Release");
649 CopyMetaIndex(CDROM
, Name
, prefix
, "Release.gpg");
655 // SigVerify::GetGPGVCommandLine - returns the command needed for verify/*{{{*/
656 // ---------------------------------------------------------------------
657 /* Generating the commandline for calling gpgv is somehow complicated as
658 we need to add multiple keyrings and user supplied options. Also, as
659 the cdrom code currently can not use the gpgv method we have two places
660 these need to be done - so the place for this method is wrong but better
661 than code duplication… */
662 std::vector
<const char *> SigVerify::GetGPGVCommandLine()
664 string
const gpgvpath
= _config
->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
665 // FIXME: remove support for deprecated APT::GPGV setting
666 string
const trustedFile
= _config
->FindFile("Dir::Etc::Trusted",
667 _config
->Find("APT::GPGV::TrustedKeyring", "/etc/apt/trusted.gpg").c_str());
668 string
const trustedPath
= _config
->FindDir("Dir::Etc::TrustedParts", "/etc/apt/trusted.gpg.d");
670 if (_config
->FindB("Debug::Acquire::gpgv", false) == true)
672 std::clog
<< "gpgv path: " << gpgvpath
<< std::endl
;
673 std::clog
<< "Keyring file: " << trustedFile
<< std::endl
;
674 std::clog
<< "Keyring path: " << trustedPath
<< std::endl
;
677 std::vector
<string
> keyrings
= GetListOfFilesInDir(trustedPath
, "gpg", false);
678 if (FileExists(trustedFile
) == true)
679 keyrings
.push_back(trustedFile
);
681 std::vector
<const char *> Args
;
684 if (keyrings
.empty() == true)
687 Args
.push_back(gpgvpath
.c_str());
688 Args
.push_back("--ignore-time-conflict");
690 for (vector
<string
>::const_iterator K
= keyrings
.begin();
691 K
!= keyrings
.end(); ++K
)
693 Args
.push_back("--keyring");
694 Args
.push_back(K
->c_str());
697 Configuration::Item
const *Opts
;
698 Opts
= _config
->Tree("Acquire::gpgv::Options");
702 for (; Opts
!= 0; Opts
= Opts
->Next
)
704 if (Opts
->Value
.empty() == true)
706 Args
.push_back(Opts
->Value
.c_str());
713 bool TranslationsCopy::CopyTranslations(string CDROM
,string Name
, /*{{{*/
714 vector
<string
> &List
, pkgCdromStatus
*log
)
716 OpProgress
*Progress
= NULL
;
717 if (List
.size() == 0)
721 Progress
= log
->GetOpProgress();
723 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
725 // Prepare the progress indicator
726 unsigned long TotalSize
= 0;
727 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
730 if (stat(string(*I
).c_str(),&Buf
) != 0 &&
731 stat(string(*I
+ ".gz").c_str(),&Buf
) != 0)
732 return _error
->Errno("stat","Stat failed for %s",
734 TotalSize
+= Buf
.st_size
;
737 unsigned long CurrentSize
= 0;
738 unsigned int NotFound
= 0;
739 unsigned int WrongSize
= 0;
740 unsigned int Packages
= 0;
741 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
743 string OrigPath
= string(*I
,CDROM
.length());
744 unsigned long FileSize
= 0;
746 // Open the package file
748 if (FileExists(*I
) == true)
750 Pkg
.Open(*I
,FileFd::ReadOnly
);
751 FileSize
= Pkg
.Size();
755 FileFd
From(*I
+ ".gz",FileFd::ReadOnly
);
756 if (_error
->PendingError() == true)
758 FileSize
= From
.Size();
761 FILE *tmp
= tmpfile();
763 return _error
->Errno("tmpfile","Unable to create a tmp file");
764 Pkg
.Fd(dup(fileno(tmp
)));
768 pid_t Process
= fork();
770 return _error
->Errno("fork","Couldn't fork gzip");
775 dup2(From
.Fd(),STDIN_FILENO
);
776 dup2(Pkg
.Fd(),STDOUT_FILENO
);
777 SetCloseExec(STDIN_FILENO
,false);
778 SetCloseExec(STDOUT_FILENO
,false);
781 string Tmp
= _config
->Find("Dir::bin::gzip","gzip");
782 Args
[0] = Tmp
.c_str();
785 execvp(Args
[0],(char **)Args
);
789 // Wait for gzip to finish
790 if (ExecWait(Process
,_config
->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
791 return _error
->Error("gzip failed, perhaps the disk is full.");
795 pkgTagFile
Parser(&Pkg
);
796 if (_error
->PendingError() == true)
799 // Open the output file
801 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s",Name
.c_str(),
802 (*I
).c_str() + CDROM
.length());
803 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
804 TargetF
+= URItoFileName(S
);
805 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
806 TargetF
= "/dev/null";
807 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
808 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
809 if (_error
->PendingError() == true)
812 return _error
->Errno("fdopen","Failed to reopen fd");
814 // Setup the progress meter
816 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
817 string("Reading Translation Indexes"));
821 Progress
->SubProgress(Pkg
.Size());
822 pkgTagSection Section
;
823 this->Section
= &Section
;
825 unsigned long Hits
= 0;
826 unsigned long Chop
= 0;
827 while (Parser
.Step(Section
) == true)
830 Progress
->Progress(Parser
.Offset());
834 Section
.GetSection(Start
,Stop
);
835 fwrite(Start
,Stop
-Start
, 1, TargetFl
);
836 fputc('\n',TargetFl
);
844 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
846 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
848 // Move out of the partial directory
850 string FinalF
= _config
->FindDir("Dir::State::lists");
851 FinalF
+= URItoFileName(S
);
852 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
853 return _error
->Errno("rename","Failed to rename");
857 CurrentSize
+= FileSize
;
865 if(NotFound
== 0 && WrongSize
== 0)
866 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
867 else if (NotFound
!= 0 && WrongSize
== 0)
868 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
870 else if (NotFound
== 0 && WrongSize
!= 0)
871 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
872 Packages
, WrongSize
);
873 if (NotFound
!= 0 && WrongSize
!= 0)
874 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
878 _error
->Warning("No valid records were found.");
880 if (NotFound
+ WrongSize
> 10)
881 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");