1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.3 1998/11/28 00:00:36 jgg Exp $
4 /* ######################################################################
6 APT CDROM - Tool for handling APT's CDROM database.
8 Currently the only option is 'add' which will take the current CD
9 in the drive and add it into the database.
11 ##################################################################### */
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/md5.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/progress.h>
20 #include <apt-pkg/tagfile.h>
29 #include <sys/errno.h>
38 // UnmountCdrom - Unmount a cdrom /*{{{*/
39 // ---------------------------------------------------------------------
41 bool UnmountCdrom(string Path
)
45 return _error
->Errno("fork","Failed to fork");
50 // Make all the fds /dev/null
51 for (int I
= 0; I
!= 10; I
++)
53 for (int I
= 0; I
!= 3; I
++)
54 dup2(open("/dev/null",O_RDWR
),I
);
58 Args
[1] = Path
.c_str();
60 execvp(Args
[0],(char **)Args
);
66 while (waitpid(Child
,&Status
,0) != Child
)
70 return _error
->Errno("waitpid","Couldn't wait for subprocess");
73 // Check for an error code.
74 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
79 // MountCdrom - Mount a cdrom /*{{{*/
80 // ---------------------------------------------------------------------
82 bool MountCdrom(string Path
)
86 return _error
->Errno("fork","Failed to fork");
91 // Make all the fds /dev/null
92 for (int I
= 0; I
!= 10; I
++)
94 for (int I
= 0; I
!= 3; I
++)
95 dup2(open("/dev/null",O_RDWR
),I
);
99 Args
[1] = Path
.c_str();
101 execvp(Args
[0],(char **)Args
);
107 while (waitpid(Child
,&Status
,0) != Child
)
111 return _error
->Errno("waitpid","Couldn't wait for subprocess");
114 // Check for an error code.
115 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
120 // IdentCdrom - Generate a unique string for this CD /*{{{*/
121 // ---------------------------------------------------------------------
122 /* We convert everything we hash into a string, this prevents byte size/order
123 from effecting the outcome. */
124 bool IdentCdrom(string CD
,string
&Res
)
128 string StartDir
= SafeGetCWD();
129 if (chdir(CD
.c_str()) != 0)
130 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
132 DIR *D
= opendir(".");
134 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
136 // Run over the directory
138 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
141 if (strcmp(Dir
->d_name
,".") == 0 ||
142 strcmp(Dir
->d_name
,"..") == 0)
145 sprintf(S
,"%lu",Dir
->d_ino
);
147 Hash
.Add(Dir
->d_name
);
150 chdir(StartDir
.c_str());
153 // Some stats from the fsys
155 if (statfs(CD
.c_str(),&Buf
) != 0)
156 return _error
->Errno("statfs","Failed to stat the cdrom");
158 sprintf(S
,"%u %u",Buf
.f_blocks
,Buf
.f_bfree
);
161 Res
= Hash
.Result().Value();
166 // FindPackages - Find the package files on the CDROM /*{{{*/
167 // ---------------------------------------------------------------------
168 /* We look over the cdrom for package files. This is a recursive
169 search that short circuits when it his a package file in the dir.
170 This speeds it up greatly as the majority of the size is in the
171 binary-* sub dirs. */
172 bool FindPackages(string CD
,vector
<string
> &List
, int Depth
= 0)
177 if (CD
[CD
.length()-1] != '/')
180 if (chdir(CD
.c_str()) != 0)
181 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
183 /* Aha! We found some package files. We assume that everything under
184 this dir is controlled by those package files so we don't look down
187 if (stat("Packages",&Buf
) == 0 ||
188 stat("Packages.gz",&Buf
) == 0)
194 DIR *D
= opendir(".");
196 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
198 // Run over the directory
199 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
202 if (strcmp(Dir
->d_name
,".") == 0 ||
203 strcmp(Dir
->d_name
,"..") == 0 ||
204 strcmp(Dir
->d_name
,"source") == 0 ||
205 strcmp(Dir
->d_name
,"experimental") == 0 ||
206 strcmp(Dir
->d_name
,"binary-all") == 0)
209 // See if the name is a sub directory
211 if (stat(Dir
->d_name
,&Buf
) != 0)
214 if (S_ISDIR(Buf
.st_mode
) == 0)
218 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
221 if (chdir(CD
.c_str()) != 0)
222 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
227 return !_error
->PendingError();
230 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
231 // ---------------------------------------------------------------------
232 /* Here we drop everything that is not this machines arch */
233 bool DropBinaryArch(vector
<string
> &List
)
236 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
238 for (unsigned int I
= 0; I
< List
.size(); I
++)
240 const char *Str
= List
[I
].c_str();
243 if ((Res
= strstr(Str
,"/binary-")) == 0)
247 if (strlen(Res
) < strlen(S
))
249 List
.erase(List
.begin() + I
);
254 // See if it is our arch
255 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
259 List
.erase(List
.begin() + I
);
266 // Score - We compute a 'score' for a path /*{{{*/
267 // ---------------------------------------------------------------------
268 /* Paths are scored based on how close they come to what I consider
269 normal. That is ones that have 'dist' 'stable' 'frozen' will score
270 higher than ones without. */
271 int Score(string Path
)
274 if (Path
.find("stable/") != string::npos
)
276 if (Path
.find("frozen/") != string::npos
)
278 if (Path
.find("/dists/") != string::npos
)
280 if (Path
.find("/main/") != string::npos
)
282 if (Path
.find("/contrib/") != string::npos
)
284 if (Path
.find("/non-free/") != string::npos
)
286 if (Path
.find("/non-US/") != string::npos
)
291 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
292 // ---------------------------------------------------------------------
293 /* Here we go and stat every file that we found and strip dup inodes. */
294 bool DropRepeats(vector
<string
> &List
)
296 // Get a list of all the inodes
297 ino_t
*Inodes
= new ino_t
[List
.size()];
298 for (unsigned int I
= 0; I
!= List
.size(); I
++)
301 if (stat(List
[I
].c_str(),&Buf
) != 0)
302 _error
->Errno("stat","Failed to stat %s",List
[I
].c_str());
303 Inodes
[I
] = Buf
.st_ino
;
307 for (unsigned int I
= 0; I
!= List
.size(); I
++)
309 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
312 if (Inodes
[J
] != Inodes
[I
])
315 // We score the two paths.. and erase one
316 int ScoreA
= Score(List
[I
]);
317 int ScoreB
= Score(List
[J
]);
328 // Wipe erased entries
329 for (unsigned int I
= 0; I
< List
.size();)
331 if (List
[I
].empty() == false)
334 List
.erase(List
.begin()+I
);
340 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
341 // ---------------------------------------------------------------------
342 /* We look for things in dists/ notation and convert them to
343 <dist> <component> form otherwise it is left alone. This also strips
345 void ConvertToSourceList(string CD
,string
&Path
)
348 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
350 // Strip the cdrom base path
351 Path
= string(Path
,CD
.length());
353 // Too short to be a dists/ type
354 if (Path
.length() < strlen("dists/"))
358 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
362 string::size_type Slash
= strlen("dists/");
363 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
364 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
366 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
368 // Isolate the component
369 Slash
= Path
.find('/',Slash2
+1);
370 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
372 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
374 // Verify the trailing binar - bit
375 Slash2
= Path
.find('/',Slash
+ 1);
376 if (Slash
== string::npos
)
378 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
383 Path
= Dist
+ ' ' + Comp
;
386 // GrabFirst - Return the first Depth path components /*{{{*/
387 // ---------------------------------------------------------------------
389 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
391 string::size_type I
= 0;
394 I
= Path
.find('/',I
+1);
397 while (I
!= string::npos
&& Depth
!= 0);
399 if (I
== string::npos
)
402 To
= string(Path
,0,I
+1);
406 // CopyPackages - Copy the package files from the CD /*{{{*/
407 // ---------------------------------------------------------------------
409 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
411 OpTextProgress Progress
;
413 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
415 // Prepare the progress indicator
416 unsigned long TotalSize
= 0;
417 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
420 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
421 return _error
->Errno("stat","Stat failed for %s",
422 string(*I
+ "Packages").c_str());
423 TotalSize
+= Buf
.st_size
;
426 unsigned long CurrentSize
= 0;
427 unsigned int NotFound
= 0;
428 unsigned int WrongSize
= 0;
429 unsigned int Packages
= 0;
430 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
432 string OrigPath
= string(*I
,CDROM
.length());
434 // Open the package file
435 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
436 pkgTagFile
Parser(Pkg
);
437 if (_error
->PendingError() == true)
440 // Open the output file
442 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
443 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
444 TargetF
+= URItoFileName(S
);
445 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
446 TargetF
= "/dev/null";
447 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
448 if (_error
->PendingError() == true)
451 // Setup the progress meter
452 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
453 "Reading Package Lists");
456 Progress
.SubProgress(Pkg
.Size());
457 pkgTagSection Section
;
459 unsigned long Hits
= 0;
460 while (Parser
.Step(Section
) == true)
462 Progress
.Progress(Parser
.Offset());
464 string File
= Section
.FindS("Filename");
465 unsigned long Size
= Section
.FindI("Size");
466 if (File
.empty() || Size
== 0)
467 return _error
->Error("Cannot find filename or size tag");
469 // See if the file exists
470 if (NoStat
== false || Hits
< 10)
473 unsigned int Depth
= 1;
474 string MyPrefix
= Prefix
;
477 if (stat(string(CDROM
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
479 if (Prefix
.empty() == true)
481 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
497 // Store the new prefix
502 if ((unsigned)Buf
.st_size
!= Size
)
512 // Copy it to the target package file
515 Section
.GetSection(Start
,Stop
);
516 if (Target
.Write(Start
,Stop
-Start
) == false)
520 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
522 // Move out of the partial directory
524 string FinalF
= _config
->FindDir("Dir::State::lists");
525 FinalF
+= URItoFileName(S
);
526 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
527 return _error
->Errno("rename","Failed to rename");
529 // Copy the release file
530 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
531 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
532 TargetF
+= URItoFileName(S
);
533 if (FileExists(*I
+ "Release") == true)
535 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
536 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
537 if (_error
->PendingError() == true)
540 if (CopyFile(Rel
,Target
) == false)
545 // Empty release file
546 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
549 // Rename the release file
550 FinalF
= _config
->FindDir("Dir::State::lists");
551 FinalF
+= URItoFileName(S
);
552 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
553 return _error
->Errno("rename","Failed to rename");
556 /* Mangle the source to be in the proper notation with
557 prefix dist [component] */
558 *I
= string(*I
,Prefix
.length());
559 ConvertToSourceList(CDROM
,*I
);
560 *I
= Prefix
+ ' ' + *I
;
562 CurrentSize
+= Pkg
.Size();
567 cout
<< "Wrote " << Packages
<< " package records" ;
569 cout
<< " with " << NotFound
<< " missing files";
570 if (NotFound
!= 0 && WrongSize
!= 0)
573 cout
<< " with " << WrongSize
<< " mismatched files";
577 return _error
->Error("No valid package records were found.");
579 if (NotFound
+ WrongSize
> 10)
580 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
584 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
585 // ---------------------------------------------------------------------
586 /* This takes the list of source list expressed entires and collects
587 similar ones to form a single entry for each dist */
588 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
590 sort(List
.begin(),List
.end());
592 // Collect similar entries
593 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
596 string::size_type Space
= (*I
).find(' ');
597 if (Space
== string::npos
)
599 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
600 if (SSpace
== string::npos
)
603 string Word1
= string(*I
,Space
,SSpace
-Space
);
604 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
607 string::size_type Space2
= (*J
).find(' ');
608 if (Space2
== string::npos
)
610 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
611 if (SSpace2
== string::npos
)
614 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
617 *J
+= string(*I
,SSpace
);
622 // Wipe erased entries
623 for (unsigned int I
= 0; I
< List
.size();)
625 if (List
[I
].empty() == false)
628 List
.erase(List
.begin()+I
);
632 // WriteDatabase - Write the CDROM Database file /*{{{*/
633 // ---------------------------------------------------------------------
634 /* We rewrite the configuration class associated with the cdrom database. */
635 bool WriteDatabase(Configuration
&Cnf
)
637 string DFile
= _config
->FindFile("Dir::State::cdroms");
638 string NewFile
= DFile
+ ".new";
640 unlink(NewFile
.c_str());
641 ofstream
Out(NewFile
.c_str());
643 return _error
->Errno("ofstream::ofstream",
644 "Failed to open %s.new",DFile
.c_str());
646 /* Write out all of the configuration directives by walking the
647 configuration tree */
648 const Configuration::Item
*Top
= Cnf
.Tree(0);
651 // Print the config entry
652 if (Top
->Value
.empty() == false)
653 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
661 while (Top
!= 0 && Top
->Next
== 0)
669 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
670 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
671 return _error
->Errno("rename","Failed to rename %s.new to %s",
672 DFile
.c_str(),DFile
.c_str());
677 // WriteSourceList - Write an updated sourcelist /*{{{*/
678 // ---------------------------------------------------------------------
679 /* This reads the old source list and copies it into the new one. It
680 appends the new CDROM entires just after the first block of comments.
681 This places them first in the file. It also removes any old entries
682 that were the same. */
683 bool WriteSourceList(string Name
,vector
<string
> &List
)
685 string File
= _config
->FindFile("Dir::Etc::sourcelist");
687 // Open the stream for reading
688 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
690 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
692 string NewFile
= File
+ ".new";
693 unlink(NewFile
.c_str());
694 ofstream
Out(NewFile
.c_str());
696 return _error
->Errno("ofstream::ofstream",
697 "Failed to open %s.new",File
.c_str());
699 // Create a short uri without the path
700 string ShortURI
= "cdrom:" + Name
+ "/";
705 while (F
.eof() == false)
707 F
.getline(Buffer
,sizeof(Buffer
));
709 _strtabexpand(Buffer
,sizeof(Buffer
));
713 if (Buffer
[0] == '#' || Buffer
[0] == 0)
715 Out
<< Buffer
<< endl
;
721 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
723 string::size_type Space
= (*I
).find(' ');
724 if (Space
== string::npos
)
725 return _error
->Error("Internal error");
727 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
728 "\" " << string(*I
,Space
+1) << endl
;
737 if (ParseQuoteWord(C
,Type
) == false ||
738 ParseQuoteWord(C
,URI
) == false)
740 Out
<< Buffer
<< endl
;
744 // Emit lines like this one
745 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
747 Out
<< Buffer
<< endl
;
752 // Just in case the file was empty
755 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
757 string::size_type Space
= (*I
).find(' ');
758 if (Space
== string::npos
)
759 return _error
->Error("Internal error");
761 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
762 "\" " << string(*I
,Space
+1) << endl
;
768 rename(File
.c_str(),string(File
+ '~').c_str());
769 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
770 return _error
->Errno("rename","Failed to rename %s.new to %s",
771 File
.c_str(),File
.c_str());
777 // Prompt - Simple prompt /*{{{*/
778 // ---------------------------------------------------------------------
780 void Prompt(const char *Text
)
783 cout
<< Text
<< ' ' << flush
;
784 read(STDIN_FILENO
,&C
,1);
789 // PromptLine - Prompt for an input line /*{{{*/
790 // ---------------------------------------------------------------------
792 string
PromptLine(const char *Text
)
794 cout
<< Text
<< ':' << endl
;
802 // DoAdd - Add a new CDROM /*{{{*/
803 // ---------------------------------------------------------------------
804 /* This does the main add bit.. We show some status and things. The
805 sequence is to mount/umount the CD, Ident it then scan it for package
806 files and reduce that list. Then we copy over the package files and
807 verify them. Then rewrite the database files */
808 bool DoAdd(CommandLine
&)
811 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
812 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
815 Configuration Database
;
816 string DFile
= _config
->FindFile("Dir::State::cdroms");
817 if (FileExists(DFile
) == true)
819 if (ReadConfigFile(Database
,DFile
) == false)
820 return _error
->Error("Unable to read the cdrom database %s",
824 // Unmount the CD and get the user to put in the one they want
825 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
827 cout
<< "Unmounting CD-ROM" << endl
;
830 // Mount the new CDROM
831 Prompt("Please insert a Disc in the drive and press any key");
832 cout
<< "Mounting CD-ROM" << endl
;
833 if (MountCdrom(CDROM
) == false)
835 cout
<< "Failed to mount the cdrom." << endl
;
840 // Hash the CD to get an ID
841 cout
<< "Identifying.. " << flush
;
843 if (IdentCdrom(CDROM
,ID
) == false)
845 cout
<< '[' << ID
<< ']' << endl
;
847 cout
<< "Scanning Disc for index files.. " << flush
;
848 // Get the CD structure
850 string StartDir
= SafeGetCWD();
851 if (FindPackages(CDROM
,List
) == false)
853 chdir(StartDir
.c_str());
855 if (_config
->FindB("Debug::aptcdrom",false) == true)
857 cout
<< "I found:" << endl
;
858 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
865 DropBinaryArch(List
);
867 cout
<< "Found " << List
.size() << " package index files." << endl
;
869 if (List
.size() == 0)
870 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
872 // Check if the CD is in the database
874 if (Database
.Exists("CD::" + ID
) == false ||
875 _config
->FindB("APT::CDROM::Rename",false) == true)
877 // Try to use the CDs label if at all possible
878 if (FileExists(CDROM
+ "/.disk/info") == true)
880 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
884 if (Name
.empty() == false)
886 cout
<< "Found label '" << Name
<< "'" << endl
;
887 Database
.Set("CD::" + ID
+ "::Label",Name
);
891 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
892 Name
.empty() == false)
894 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
897 Name
= PromptLine("");
898 if (Name
.empty() == false &&
899 Name
.find('/') == string::npos
)
901 cout
<< "That is not a valid name, try again " << endl
;
907 Name
= Database
.Find("CD::" + ID
);
908 Database
.Set("CD::" + ID
,Name
);
909 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
911 // Copy the package files to the state directory
912 if (CopyPackages(CDROM
,Name
,List
) == false)
915 ReduceSourcelist(CDROM
,List
);
917 // Write the database and sourcelist
918 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
920 if (WriteDatabase(Database
) == false)
923 cout
<< "Writing new source list" << endl
;
924 if (WriteSourceList(Name
,List
) == false)
928 // Print the sourcelist entries
929 cout
<< "Source List entires for this Disc are:" << endl
;
930 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
932 string::size_type Space
= (*I
).find(' ');
933 if (Space
== string::npos
)
934 return _error
->Error("Internal error");
936 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
937 "\" " << string(*I
,Space
+1) << endl
;
944 // ShowHelp - Show the help screen /*{{{*/
945 // ---------------------------------------------------------------------
949 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
950 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
952 cout
<< "Usage: apt-cdrom [options] command" << endl
;
954 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
955 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
956 cout
<< "and /etc/fstab." << endl
;
958 cout
<< "Commands:" << endl
;
959 cout
<< " add - Add a CDROM" << endl
;
961 cout
<< "Options:" << endl
;
962 cout
<< " -h This help text" << endl
;
963 cout
<< " -d CD-ROM mount point" << endl
;
964 cout
<< " -r Rename a recognized CD-ROM" << endl
;
965 cout
<< " -m No mounting" << endl
;
966 cout
<< " -f Fast mode, don't check package files" << endl
;
967 cout
<< " -c=? Read this configuration file" << endl
;
968 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
969 cout
<< "See fstab(5)" << endl
;
974 int main(int argc
,const char *argv
[])
976 CommandLine::Args Args
[] = {
977 {'h',"help","help",0},
978 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
979 {'r',"rename","APT::CDROM::Rename",0},
980 {'m',"no-mount","APT::CDROM::NoMount",0},
981 {'f',"fast","APT::CDROM::Fast",0},
982 {'n',"just-print","APT::CDROM::NoAct",0},
983 {'n',"recon","APT::CDROM::NoAct",0},
984 {'n',"no-act","APT::CDROM::NoAct",0},
985 {'c',"config-file",0,CommandLine::ConfigFile
},
986 {'o',"option",0,CommandLine::ArbItem
},
988 CommandLine::Dispatch Cmds
[] = {
992 // Parse the command line and initialize the package library
993 CommandLine
CmdL(Args
,_config
);
994 if (pkgInitialize(*_config
) == false ||
995 CmdL
.Parse(argc
,argv
) == false)
997 _error
->DumpErrors();
1001 // See if the help should be shown
1002 if (_config
->FindB("help") == true ||
1003 CmdL
.FileSize() == 0)
1006 // Match the operation
1007 CmdL
.DispatchArg(Cmds
);
1009 // Print any errors or warnings found during parsing
1010 if (_error
->empty() == false)
1012 bool Errors
= _error
->PendingError();
1013 _error
->DumpErrors();
1014 return Errors
== true?100:0;