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