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