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