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