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