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