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