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