]> git.saurik.com Git - apt.git/blob - apt-pkg/cdrom.cc
* fix FTBFS with gcc 4.3, closes: #417090
[apt.git] / apt-pkg / cdrom.cc
1 /*
2 */
3
4 #ifdef __GNUG__
5 #pragma implementation "apt-pkg/cdrom.h"
6 #endif
7 #include<apt-pkg/init.h>
8 #include<apt-pkg/error.h>
9 #include<apt-pkg/cdromutl.h>
10 #include<apt-pkg/strutl.h>
11 #include<apt-pkg/cdrom.h>
12 #include<sstream>
13 #include<fstream>
14 #include<config.h>
15 #include<apti18n.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <dirent.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <algorithm>
22
23
24 #include "indexcopy.h"
25
26 using namespace std;
27
28 // FindPackages - Find the package files on the CDROM /*{{{*/
29 // ---------------------------------------------------------------------
30 /* We look over the cdrom for package files. This is a recursive
31 search that short circuits when it his a package file in the dir.
32 This speeds it up greatly as the majority of the size is in the
33 binary-* sub dirs. */
34 bool pkgCdrom::FindPackages(string CD,vector<string> &List,
35 vector<string> &SList, vector<string> &SigList,
36 string &InfoDir, pkgCdromStatus *log,
37 unsigned int Depth)
38 {
39 static ino_t Inodes[9];
40
41 // if we have a look we "pulse" now
42 if(log)
43 log->Update();
44
45 if (Depth >= 7)
46 return true;
47
48 if (CD[CD.length()-1] != '/')
49 CD += '/';
50
51 if (chdir(CD.c_str()) != 0)
52 return _error->Errno("chdir","Unable to change to %s",CD.c_str());
53
54 // Look for a .disk subdirectory
55 struct stat Buf;
56 if (stat(".disk",&Buf) == 0)
57 {
58 if (InfoDir.empty() == true)
59 InfoDir = CD + ".disk/";
60 }
61
62 // Don't look into directories that have been marked to ingore.
63 if (stat(".aptignr",&Buf) == 0)
64 return true;
65
66
67 /* Check _first_ for a signature file as apt-cdrom assumes that all files
68 under a Packages/Source file are in control of that file and stops
69 the scanning
70 */
71 if (stat("Release.gpg",&Buf) == 0)
72 {
73 SigList.push_back(CD);
74 }
75 /* Aha! We found some package files. We assume that everything under
76 this dir is controlled by those package files so we don't look down
77 anymore */
78 if (stat("Packages",&Buf) == 0 || stat("Packages.gz",&Buf) == 0)
79 {
80 List.push_back(CD);
81
82 // Continue down if thorough is given
83 if (_config->FindB("APT::CDROM::Thorough",false) == false)
84 return true;
85 }
86 if (stat("Sources.gz",&Buf) == 0 || stat("Sources",&Buf) == 0)
87 {
88 SList.push_back(CD);
89
90 // Continue down if thorough is given
91 if (_config->FindB("APT::CDROM::Thorough",false) == false)
92 return true;
93 }
94
95 DIR *D = opendir(".");
96 if (D == 0)
97 return _error->Errno("opendir","Unable to read %s",CD.c_str());
98
99 // Run over the directory
100 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
101 {
102 // Skip some files..
103 if (strcmp(Dir->d_name,".") == 0 ||
104 strcmp(Dir->d_name,"..") == 0 ||
105 //strcmp(Dir->d_name,"source") == 0 ||
106 strcmp(Dir->d_name,".disk") == 0 ||
107 strcmp(Dir->d_name,"experimental") == 0 ||
108 strcmp(Dir->d_name,"binary-all") == 0 ||
109 strcmp(Dir->d_name,"debian-installer") == 0)
110 continue;
111
112 // See if the name is a sub directory
113 struct stat Buf;
114 if (stat(Dir->d_name,&Buf) != 0)
115 continue;
116
117 if (S_ISDIR(Buf.st_mode) == 0)
118 continue;
119
120 unsigned int I;
121 for (I = 0; I != Depth; I++)
122 if (Inodes[I] == Buf.st_ino)
123 break;
124 if (I != Depth)
125 continue;
126
127 // Store the inodes weve seen
128 Inodes[Depth] = Buf.st_ino;
129
130 // Descend
131 if (FindPackages(CD + Dir->d_name,List,SList,SigList,InfoDir,log,Depth+1) == false)
132 break;
133
134 if (chdir(CD.c_str()) != 0)
135 return _error->Errno("chdir","Unable to change to %s",CD.c_str());
136 };
137
138 closedir(D);
139
140 return !_error->PendingError();
141 }
142
143 // Score - We compute a 'score' for a path /*{{{*/
144 // ---------------------------------------------------------------------
145 /* Paths are scored based on how close they come to what I consider
146 normal. That is ones that have 'dist' 'stable' 'testing' will score
147 higher than ones without. */
148 int pkgCdrom::Score(string Path)
149 {
150 int Res = 0;
151 if (Path.find("stable/") != string::npos)
152 Res += 29;
153 if (Path.find("/binary-") != string::npos)
154 Res += 20;
155 if (Path.find("testing/") != string::npos)
156 Res += 28;
157 if (Path.find("unstable/") != string::npos)
158 Res += 27;
159 if (Path.find("/dists/") != string::npos)
160 Res += 40;
161 if (Path.find("/main/") != string::npos)
162 Res += 20;
163 if (Path.find("/contrib/") != string::npos)
164 Res += 20;
165 if (Path.find("/non-free/") != string::npos)
166 Res += 20;
167 if (Path.find("/non-US/") != string::npos)
168 Res += 20;
169 if (Path.find("/source/") != string::npos)
170 Res += 10;
171 if (Path.find("/debian/") != string::npos)
172 Res -= 10;
173
174 // check for symlinks in the patch leading to the actual file
175 // a symlink gets a big penalty
176 struct stat Buf;
177 string statPath = flNotFile(Path);
178 string cdromPath = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
179 while(statPath != cdromPath && statPath != "./") {
180 statPath.resize(statPath.size()-1); // remove the trailing '/'
181 if (lstat(statPath.c_str(),&Buf) == 0) {
182 if(S_ISLNK(Buf.st_mode)) {
183 Res -= 60;
184 break;
185 }
186 }
187 statPath = flNotFile(statPath); // descent
188 }
189
190 return Res;
191 }
192
193 /*}}}*/
194 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
195 // ---------------------------------------------------------------------
196 /* Here we drop everything that is not this machines arch */
197 bool pkgCdrom::DropBinaryArch(vector<string> &List)
198 {
199 char S[300];
200 snprintf(S,sizeof(S),"/binary-%s/",
201 _config->Find("Apt::Architecture").c_str());
202
203 for (unsigned int I = 0; I < List.size(); I++)
204 {
205 const char *Str = List[I].c_str();
206
207 const char *Res;
208 if ((Res = strstr(Str,"/binary-")) == 0)
209 continue;
210
211 // Weird, remove it.
212 if (strlen(Res) < strlen(S))
213 {
214 List.erase(List.begin() + I);
215 I--;
216 continue;
217 }
218
219 // See if it is our arch
220 if (stringcmp(Res,Res + strlen(S),S) == 0)
221 continue;
222
223 // Erase it
224 List.erase(List.begin() + I);
225 I--;
226 }
227
228 return true;
229 }
230
231
232 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
233 // ---------------------------------------------------------------------
234 /* Here we go and stat every file that we found and strip dup inodes. */
235 bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
236 {
237 // Get a list of all the inodes
238 ino_t *Inodes = new ino_t[List.size()];
239 for (unsigned int I = 0; I != List.size(); I++)
240 {
241 struct stat Buf;
242 if (stat((List[I] + Name).c_str(),&Buf) != 0 &&
243 stat((List[I] + Name + ".gz").c_str(),&Buf) != 0)
244 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(),
245 Name);
246 Inodes[I] = Buf.st_ino;
247 }
248
249 if (_error->PendingError() == true)
250 return false;
251
252 // Look for dups
253 for (unsigned int I = 0; I != List.size(); I++)
254 {
255 for (unsigned int J = I+1; J < List.size(); J++)
256 {
257 // No match
258 if (Inodes[J] != Inodes[I])
259 continue;
260
261 // We score the two paths.. and erase one
262 int ScoreA = Score(List[I]);
263 int ScoreB = Score(List[J]);
264 if (ScoreA < ScoreB)
265 {
266 List[I] = string();
267 break;
268 }
269
270 List[J] = string();
271 }
272 }
273
274 // Wipe erased entries
275 for (unsigned int I = 0; I < List.size();)
276 {
277 if (List[I].empty() == false)
278 I++;
279 else
280 List.erase(List.begin()+I);
281 }
282
283 return true;
284 }
285 /*}}}*/
286
287 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This takes the list of source list expressed entires and collects
290 similar ones to form a single entry for each dist */
291 void pkgCdrom::ReduceSourcelist(string CD,vector<string> &List)
292 {
293 sort(List.begin(),List.end());
294
295 // Collect similar entries
296 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
297 {
298 // Find a space..
299 string::size_type Space = (*I).find(' ');
300 if (Space == string::npos)
301 continue;
302 string::size_type SSpace = (*I).find(' ',Space + 1);
303 if (SSpace == string::npos)
304 continue;
305
306 string Word1 = string(*I,Space,SSpace-Space);
307 string Prefix = string(*I,0,Space);
308 for (vector<string>::iterator J = List.begin(); J != I; J++)
309 {
310 // Find a space..
311 string::size_type Space2 = (*J).find(' ');
312 if (Space2 == string::npos)
313 continue;
314 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
315 if (SSpace2 == string::npos)
316 continue;
317
318 if (string(*J,0,Space2) != Prefix)
319 continue;
320 if (string(*J,Space2,SSpace2-Space2) != Word1)
321 continue;
322
323 *J += string(*I,SSpace);
324 *I = string();
325 }
326 }
327
328 // Wipe erased entries
329 for (unsigned int I = 0; I < List.size();)
330 {
331 if (List[I].empty() == false)
332 I++;
333 else
334 List.erase(List.begin()+I);
335 }
336 }
337 /*}}}*/
338 // WriteDatabase - Write the CDROM Database file /*{{{*/
339 // ---------------------------------------------------------------------
340 /* We rewrite the configuration class associated with the cdrom database. */
341 bool pkgCdrom::WriteDatabase(Configuration &Cnf)
342 {
343 string DFile = _config->FindFile("Dir::State::cdroms");
344 string NewFile = DFile + ".new";
345
346 unlink(NewFile.c_str());
347 ofstream Out(NewFile.c_str());
348 if (!Out)
349 return _error->Errno("ofstream::ofstream",
350 "Failed to open %s.new",DFile.c_str());
351
352 /* Write out all of the configuration directives by walking the
353 configuration tree */
354 const Configuration::Item *Top = Cnf.Tree(0);
355 for (; Top != 0;)
356 {
357 // Print the config entry
358 if (Top->Value.empty() == false)
359 Out << Top->FullTag() + " \"" << Top->Value << "\";" << endl;
360
361 if (Top->Child != 0)
362 {
363 Top = Top->Child;
364 continue;
365 }
366
367 while (Top != 0 && Top->Next == 0)
368 Top = Top->Parent;
369 if (Top != 0)
370 Top = Top->Next;
371 }
372
373 Out.close();
374
375 rename(DFile.c_str(),string(DFile + '~').c_str());
376 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
377 return _error->Errno("rename","Failed to rename %s.new to %s",
378 DFile.c_str(),DFile.c_str());
379
380 return true;
381 }
382 /*}}}*/
383 // WriteSourceList - Write an updated sourcelist /*{{{*/
384 // ---------------------------------------------------------------------
385 /* This reads the old source list and copies it into the new one. It
386 appends the new CDROM entires just after the first block of comments.
387 This places them first in the file. It also removes any old entries
388 that were the same. */
389 bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
390 {
391 if (List.size() == 0)
392 return true;
393
394 string File = _config->FindFile("Dir::Etc::sourcelist");
395
396 // Open the stream for reading
397 ifstream F((FileExists(File)?File.c_str():"/dev/null"),
398 ios::in );
399 if (!F != 0)
400 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
401
402 string NewFile = File + ".new";
403 unlink(NewFile.c_str());
404 ofstream Out(NewFile.c_str());
405 if (!Out)
406 return _error->Errno("ofstream::ofstream",
407 "Failed to open %s.new",File.c_str());
408
409 // Create a short uri without the path
410 string ShortURI = "cdrom:[" + Name + "]/";
411 string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
412
413 string Type;
414 if (Source == true)
415 Type = "deb-src";
416 else
417 Type = "deb";
418
419 char Buffer[300];
420 int CurLine = 0;
421 bool First = true;
422 while (F.eof() == false)
423 {
424 F.getline(Buffer,sizeof(Buffer));
425 CurLine++;
426 if (F.fail() && !F.eof())
427 return _error->Error(_("Line %u too long in source list %s."),
428 CurLine,File.c_str());
429 _strtabexpand(Buffer,sizeof(Buffer));
430 _strstrip(Buffer);
431
432 // Comment or blank
433 if (Buffer[0] == '#' || Buffer[0] == 0)
434 {
435 Out << Buffer << endl;
436 continue;
437 }
438
439 if (First == true)
440 {
441 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
442 {
443 string::size_type Space = (*I).find(' ');
444 if (Space == string::npos)
445 return _error->Error("Internal error");
446 Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
447 " " << string(*I,Space+1) << endl;
448 }
449 }
450 First = false;
451
452 // Grok it
453 string cType;
454 string URI;
455 const char *C = Buffer;
456 if (ParseQuoteWord(C,cType) == false ||
457 ParseQuoteWord(C,URI) == false)
458 {
459 Out << Buffer << endl;
460 continue;
461 }
462
463 // Emit lines like this one
464 if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
465 string(URI,0,ShortURI.length()) != ShortURI2))
466 {
467 Out << Buffer << endl;
468 continue;
469 }
470 }
471
472 // Just in case the file was empty
473 if (First == true)
474 {
475 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
476 {
477 string::size_type Space = (*I).find(' ');
478 if (Space == string::npos)
479 return _error->Error("Internal error");
480
481 Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
482 " " << string(*I,Space+1) << endl;
483 }
484 }
485
486 Out.close();
487
488 rename(File.c_str(),string(File + '~').c_str());
489 if (rename(NewFile.c_str(),File.c_str()) != 0)
490 return _error->Errno("rename","Failed to rename %s.new to %s",
491 File.c_str(),File.c_str());
492
493 return true;
494 }
495
496
497 bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log)
498 {
499 stringstream msg;
500
501 // Startup
502 string CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
503 if (CDROM[0] == '.')
504 CDROM= SafeGetCWD() + '/' + CDROM;
505
506 if(log) {
507 msg.str("");
508 ioprintf(msg, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
509 CDROM.c_str());
510 log->Update(msg.str());
511 }
512 if (MountCdrom(CDROM) == false)
513 return _error->Error("Failed to mount the cdrom.");
514
515 // Hash the CD to get an ID
516 if(log)
517 log->Update(_("Identifying.. "));
518
519
520 if (IdentCdrom(CDROM,ident) == false)
521 {
522 ident = "";
523 return false;
524 }
525
526 msg.str("");
527 ioprintf(msg, "[%s]\n",ident.c_str());
528 log->Update(msg.str());
529
530
531 // Read the database
532 Configuration Database;
533 string DFile = _config->FindFile("Dir::State::cdroms");
534 if (FileExists(DFile) == true)
535 {
536 if (ReadConfigFile(Database,DFile) == false)
537 return _error->Error("Unable to read the cdrom database %s",
538 DFile.c_str());
539 }
540 if(log) {
541 msg.str("");
542 ioprintf(msg, _("Stored label: %s \n"),
543 Database.Find("CD::"+ident).c_str());
544 log->Update(msg.str());
545 }
546 return true;
547 }
548
549
550 bool pkgCdrom::Add(pkgCdromStatus *log)
551 {
552 stringstream msg;
553
554 // Startup
555 string CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
556 if (CDROM[0] == '.')
557 CDROM= SafeGetCWD() + '/' + CDROM;
558
559 if(log) {
560 log->SetTotal(STEP_LAST);
561 msg.str("");
562 ioprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
563 log->Update(msg.str(), STEP_PREPARE);
564 }
565
566 // Read the database
567 Configuration Database;
568 string DFile = _config->FindFile("Dir::State::cdroms");
569 if (FileExists(DFile) == true)
570 {
571 if (ReadConfigFile(Database,DFile) == false)
572 return _error->Error("Unable to read the cdrom database %s",
573 DFile.c_str());
574 }
575
576 // Unmount the CD and get the user to put in the one they want
577 if (_config->FindB("APT::CDROM::NoMount",false) == false)
578 {
579 if(log)
580 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
581 UnmountCdrom(CDROM);
582
583 if(log) {
584 log->Update(_("Waiting for disc...\n"), STEP_WAIT);
585 if(!log->ChangeCdrom()) {
586 // user aborted
587 return false;
588 }
589 }
590
591 // Mount the new CDROM
592 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
593 if (MountCdrom(CDROM) == false)
594 return _error->Error("Failed to mount the cdrom.");
595 }
596
597 // Hash the CD to get an ID
598 if(log)
599 log->Update(_("Identifying.. "), STEP_IDENT);
600 string ID;
601 if (IdentCdrom(CDROM,ID) == false)
602 {
603 log->Update("\n");
604 return false;
605 }
606 if(log)
607 log->Update("["+ID+"]\n");
608
609 if(log)
610 log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
611
612 // Get the CD structure
613 vector<string> List;
614 vector<string> SourceList;
615 vector<string> SigList;
616 string StartDir = SafeGetCWD();
617 string InfoDir;
618 if (FindPackages(CDROM,List,SourceList, SigList,InfoDir,log) == false)
619 {
620 log->Update("\n");
621 return false;
622 }
623
624 chdir(StartDir.c_str());
625
626 if (_config->FindB("Debug::aptcdrom",false) == true)
627 {
628 cout << "I found (binary):" << endl;
629 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
630 cout << *I << endl;
631 cout << "I found (source):" << endl;
632 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); I++)
633 cout << *I << endl;
634 cout << "I found (Signatures):" << endl;
635 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); I++)
636 cout << *I << endl;
637 }
638
639 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
640
641 // Fix up the list
642 DropBinaryArch(List);
643 DropRepeats(List,"Packages");
644 DropRepeats(SourceList,"Sources");
645 DropRepeats(SigList,"Release.gpg");
646 if(log) {
647 msg.str("");
648 ioprintf(msg, _("Found %i package indexes, %i source indexes and "
649 "%i signatures\n"),
650 List.size(), SourceList.size(), SigList.size());
651 log->Update(msg.str(), STEP_SCAN);
652 }
653
654 if (List.size() == 0 && SourceList.size() == 0)
655 {
656 if (_config->FindB("APT::CDROM::NoMount",false) == false)
657 UnmountCdrom(CDROM);
658 return _error->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
659 }
660
661 // Check if the CD is in the database
662 string Name;
663 if (Database.Exists("CD::" + ID) == false ||
664 _config->FindB("APT::CDROM::Rename",false) == true)
665 {
666 // Try to use the CDs label if at all possible
667 if (InfoDir.empty() == false &&
668 FileExists(InfoDir + "/info") == true)
669 {
670 ifstream F(string(InfoDir + "/info").c_str());
671 if (!F == 0)
672 getline(F,Name);
673
674 if (Name.empty() == false)
675 {
676 // Escape special characters
677 string::iterator J = Name.begin();
678 for (; J != Name.end(); J++)
679 if (*J == '"' || *J == ']' || *J == '[')
680 *J = '_';
681
682 if(log) {
683 msg.str("");
684 ioprintf(msg, _("Found label '%s'\n"), Name.c_str());
685 log->Update(msg.str());
686 }
687 Database.Set("CD::" + ID + "::Label",Name);
688 }
689 }
690
691 if (_config->FindB("APT::CDROM::Rename",false) == true ||
692 Name.empty() == true)
693 {
694 if(!log)
695 {
696 if (_config->FindB("APT::CDROM::NoMount",false) == false)
697 UnmountCdrom(CDROM);
698 return _error->Error("No disc name found and no way to ask for it");
699 }
700
701 while(true) {
702 if(!log->AskCdromName(Name)) {
703 // user canceld
704 return false;
705 }
706 cout << "Name: '" << Name << "'" << endl;
707
708 if (Name.empty() == false &&
709 Name.find('"') == string::npos &&
710 Name.find('[') == string::npos &&
711 Name.find(']') == string::npos)
712 break;
713 log->Update(_("That is not a valid name, try again.\n"));
714 }
715 }
716 }
717 else
718 Name = Database.Find("CD::" + ID);
719
720 // Escape special characters
721 string::iterator J = Name.begin();
722 for (; J != Name.end(); J++)
723 if (*J == '"' || *J == ']' || *J == '[')
724 *J = '_';
725
726 Database.Set("CD::" + ID,Name);
727 if(log) {
728 msg.str("");
729 ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
730 log->Update(msg.str());
731 }
732
733 log->Update(_("Copying package lists..."), STEP_COPY);
734 // take care of the signatures and copy them if they are ok
735 // (we do this before PackageCopy as it modifies "List" and "SourceList")
736 SigVerify SignVerify;
737 SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
738
739 // Copy the package files to the state directory
740 PackageCopy Copy;
741 SourceCopy SrcCopy;
742 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
743 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false)
744 return false;
745
746 // reduce the List so that it takes less space in sources.list
747 ReduceSourcelist(CDROM,List);
748 ReduceSourcelist(CDROM,SourceList);
749
750 // Write the database and sourcelist
751 if (_config->FindB("APT::cdrom::NoAct",false) == false)
752 {
753 if (WriteDatabase(Database) == false)
754 return false;
755
756 if(log) {
757 log->Update(_("Writing new source list\n"), STEP_WRITE);
758 }
759 if (WriteSourceList(Name,List,false) == false ||
760 WriteSourceList(Name,SourceList,true) == false)
761 return false;
762 }
763
764 // Print the sourcelist entries
765 if(log)
766 log->Update(_("Source list entries for this disc are:\n"));
767
768 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
769 {
770 string::size_type Space = (*I).find(' ');
771 if (Space == string::npos)
772 {
773 if (_config->FindB("APT::CDROM::NoMount",false) == false)
774 UnmountCdrom(CDROM);
775 return _error->Error("Internal error");
776 }
777
778 if(log) {
779 msg.str("");
780 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
781 " " << string(*I,Space+1) << endl;
782 log->Update(msg.str());
783 }
784 }
785
786 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); I++)
787 {
788 string::size_type Space = (*I).find(' ');
789 if (Space == string::npos)
790 {
791 if (_config->FindB("APT::CDROM::NoMount",false) == false)
792 UnmountCdrom(CDROM);
793 return _error->Error("Internal error");
794 }
795
796 if(log) {
797 msg.str("");
798 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
799 " " << string(*I,Space+1) << endl;
800 log->Update(msg.str());
801 }
802 }
803
804
805
806 // Unmount and finish
807 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
808 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
809 UnmountCdrom(CDROM);
810 }
811
812 return true;
813 }