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