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