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