]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.8 1998/12/08 23:52:23 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/fileutl.h>
18 #include <apt-pkg/progress.h>
19 #include <apt-pkg/tagfile.h>
20 #include <apt-pkg/cdromutl.h>
35 // FindPackages - Find the package files on the CDROM /*{{{*/
36 // ---------------------------------------------------------------------
37 /* We look over the cdrom for package files. This is a recursive
38 search that short circuits when it his a package file in the dir.
39 This speeds it up greatly as the majority of the size is in the
41 bool FindPackages(string CD
,vector
<string
> &List
, int Depth
= 0)
46 if (CD
[CD
.length()-1] != '/')
49 if (chdir(CD
.c_str()) != 0)
50 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
52 /* Aha! We found some package files. We assume that everything under
53 this dir is controlled by those package files so we don't look down
56 if (stat("Packages",&Buf
) == 0 ||
57 stat("Packages.gz",&Buf
) == 0)
61 // Continue down if thorough is given
62 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
66 DIR *D
= opendir(".");
68 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
70 // Run over the directory
71 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
74 if (strcmp(Dir
->d_name
,".") == 0 ||
75 strcmp(Dir
->d_name
,"..") == 0 ||
76 strcmp(Dir
->d_name
,"source") == 0 ||
77 strcmp(Dir
->d_name
,"experimental") == 0 ||
78 strcmp(Dir
->d_name
,"binary-all") == 0)
81 // See if the name is a sub directory
83 if (stat(Dir
->d_name
,&Buf
) != 0)
86 if (S_ISDIR(Buf
.st_mode
) == 0)
90 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
93 if (chdir(CD
.c_str()) != 0)
94 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
99 return !_error
->PendingError();
102 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
103 // ---------------------------------------------------------------------
104 /* Here we drop everything that is not this machines arch */
105 bool DropBinaryArch(vector
<string
> &List
)
108 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
110 for (unsigned int I
= 0; I
< List
.size(); I
++)
112 const char *Str
= List
[I
].c_str();
115 if ((Res
= strstr(Str
,"/binary-")) == 0)
119 if (strlen(Res
) < strlen(S
))
121 List
.erase(List
.begin() + I
);
126 // See if it is our arch
127 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
131 List
.erase(List
.begin() + I
);
138 // Score - We compute a 'score' for a path /*{{{*/
139 // ---------------------------------------------------------------------
140 /* Paths are scored based on how close they come to what I consider
141 normal. That is ones that have 'dist' 'stable' 'frozen' will score
142 higher than ones without. */
143 int Score(string Path
)
146 if (Path
.find("stable/") != string::npos
)
148 if (Path
.find("frozen/") != string::npos
)
150 if (Path
.find("/dists/") != string::npos
)
152 if (Path
.find("/main/") != string::npos
)
154 if (Path
.find("/contrib/") != string::npos
)
156 if (Path
.find("/non-free/") != string::npos
)
158 if (Path
.find("/non-US/") != string::npos
)
163 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
164 // ---------------------------------------------------------------------
165 /* Here we go and stat every file that we found and strip dup inodes. */
166 bool DropRepeats(vector
<string
> &List
)
168 // Get a list of all the inodes
169 ino_t
*Inodes
= new ino_t
[List
.size()];
170 for (unsigned int I
= 0; I
!= List
.size(); I
++)
173 if (stat(List
[I
].c_str(),&Buf
) != 0)
174 _error
->Errno("stat","Failed to stat %s",List
[I
].c_str());
175 Inodes
[I
] = Buf
.st_ino
;
179 for (unsigned int I
= 0; I
!= List
.size(); I
++)
181 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
184 if (Inodes
[J
] != Inodes
[I
])
187 // We score the two paths.. and erase one
188 int ScoreA
= Score(List
[I
]);
189 int ScoreB
= Score(List
[J
]);
200 // Wipe erased entries
201 for (unsigned int I
= 0; I
< List
.size();)
203 if (List
[I
].empty() == false)
206 List
.erase(List
.begin()+I
);
212 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
213 // ---------------------------------------------------------------------
214 /* We look for things in dists/ notation and convert them to
215 <dist> <component> form otherwise it is left alone. This also strips
217 void ConvertToSourceList(string CD
,string
&Path
)
220 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
222 // Strip the cdrom base path
223 Path
= string(Path
,CD
.length());
224 if (Path
.empty() == true)
227 // Too short to be a dists/ type
228 if (Path
.length() < strlen("dists/"))
232 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
236 string::size_type Slash
= strlen("dists/");
237 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
238 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
240 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
242 // Isolate the component
243 Slash
= Path
.find('/',Slash2
+1);
244 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
246 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
248 // Verify the trailing binar - bit
249 Slash2
= Path
.find('/',Slash
+ 1);
250 if (Slash
== string::npos
)
252 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
257 Path
= Dist
+ ' ' + Comp
;
260 // GrabFirst - Return the first Depth path components /*{{{*/
261 // ---------------------------------------------------------------------
263 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
265 string::size_type I
= 0;
268 I
= Path
.find('/',I
+1);
271 while (I
!= string::npos
&& Depth
!= 0);
273 if (I
== string::npos
)
276 To
= string(Path
,0,I
+1);
280 // ChopDirs - Chop off the leading directory components /*{{{*/
281 // ---------------------------------------------------------------------
283 string
ChopDirs(string Path
,unsigned int Depth
)
285 string::size_type I
= 0;
288 I
= Path
.find('/',I
+1);
291 while (I
!= string::npos
&& Depth
!= 0);
293 if (I
== string::npos
)
296 return string(Path
,I
+1);
299 // ReconstructPrefix - Fix strange prefixing /*{{{*/
300 // ---------------------------------------------------------------------
301 /* This prepends dir components from the path to the package files to
302 the path to the deb until it is found */
303 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
306 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
307 unsigned int Depth
= 1;
308 string MyPrefix
= Prefix
;
312 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
315 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
316 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
330 // ReconstructChop - Fixes bad source paths /*{{{*/
331 // ---------------------------------------------------------------------
332 /* This removes path components from the filename and prepends the location
333 of the package files until a file is found */
334 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
336 // Attempt to reconstruct the filename
337 unsigned long Depth
= 0;
341 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
343 File
= ChopDirs(File
,1);
345 if (File
.empty() == false)
359 // CopyPackages - Copy the package files from the CD /*{{{*/
360 // ---------------------------------------------------------------------
362 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
364 OpTextProgress Progress
;
366 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
367 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
369 // Prepare the progress indicator
370 unsigned long TotalSize
= 0;
371 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
374 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
375 return _error
->Errno("stat","Stat failed for %s",
376 string(*I
+ "Packages").c_str());
377 TotalSize
+= Buf
.st_size
;
380 unsigned long CurrentSize
= 0;
381 unsigned int NotFound
= 0;
382 unsigned int WrongSize
= 0;
383 unsigned int Packages
= 0;
384 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
386 string OrigPath
= string(*I
,CDROM
.length());
388 // Open the package file
389 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
390 pkgTagFile
Parser(Pkg
);
391 if (_error
->PendingError() == true)
394 // Open the output file
396 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
397 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
398 TargetF
+= URItoFileName(S
);
399 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
400 TargetF
= "/dev/null";
401 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
402 if (_error
->PendingError() == true)
405 // Setup the progress meter
406 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
407 "Reading Package Lists");
410 Progress
.SubProgress(Pkg
.Size());
411 pkgTagSection Section
;
413 unsigned long Hits
= 0;
414 unsigned long Chop
= 0;
415 while (Parser
.Step(Section
) == true)
417 Progress
.Progress(Parser
.Offset());
419 string File
= Section
.FindS("Filename");
420 unsigned long Size
= Section
.FindI("Size");
421 if (File
.empty() || Size
== 0)
422 return _error
->Error("Cannot find filename or size tag");
425 File
= OrigPath
+ ChopDirs(File
,Chop
);
427 // See if the file exists
428 if (NoStat
== false || Hits
< 10)
430 // Attempt to fix broken structure
433 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
434 ReconstructChop(Chop
,*I
,File
) == false)
440 File
= OrigPath
+ ChopDirs(File
,Chop
);
445 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
452 if ((unsigned)Buf
.st_size
!= Size
)
462 // Copy it to the target package file
467 // Mangle the output filename
468 const char *Filename
;
469 Section
.Find("Filename",Filename
,Stop
);
471 /* We need to rewrite the filename field so we emit
472 all fields except the filename file and rewrite that one */
473 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
475 Section
.Get(Start
,Stop
,I
);
476 if (Start
<= Filename
&& Stop
> Filename
)
479 sprintf(S
,"Filename: %s\n",File
.c_str());
480 if (I
+ 1 == Section
.Count())
482 if (Target
.Write(S
,strlen(S
)) == false)
486 if (Target
.Write(Start
,Stop
-Start
) == false)
489 if (Target
.Write("\n",1) == false)
494 Section
.GetSection(Start
,Stop
);
495 if (Target
.Write(Start
,Stop
-Start
) == false)
501 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
503 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
505 // Move out of the partial directory
507 string FinalF
= _config
->FindDir("Dir::State::lists");
508 FinalF
+= URItoFileName(S
);
509 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
510 return _error
->Errno("rename","Failed to rename");
512 // Copy the release file
513 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
514 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
515 TargetF
+= URItoFileName(S
);
516 if (FileExists(*I
+ "Release") == true)
518 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
519 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
520 if (_error
->PendingError() == true)
523 if (CopyFile(Rel
,Target
) == false)
528 // Empty release file
529 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
532 // Rename the release file
533 FinalF
= _config
->FindDir("Dir::State::lists");
534 FinalF
+= URItoFileName(S
);
535 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
536 return _error
->Errno("rename","Failed to rename");
539 /* Mangle the source to be in the proper notation with
540 prefix dist [component] */
541 *I
= string(*I
,Prefix
.length());
542 ConvertToSourceList(CDROM
,*I
);
543 *I
= Prefix
+ ' ' + *I
;
545 CurrentSize
+= Pkg
.Size();
550 cout
<< "Wrote " << Packages
<< " package records" ;
552 cout
<< " with " << NotFound
<< " missing files";
553 if (NotFound
!= 0 && WrongSize
!= 0)
556 cout
<< " with " << WrongSize
<< " mismatched files";
560 return _error
->Error("No valid package records were found.");
562 if (NotFound
+ WrongSize
> 10)
563 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
569 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
570 // ---------------------------------------------------------------------
571 /* This takes the list of source list expressed entires and collects
572 similar ones to form a single entry for each dist */
573 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
575 sort(List
.begin(),List
.end());
577 // Collect similar entries
578 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
581 string::size_type Space
= (*I
).find(' ');
582 if (Space
== string::npos
)
584 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
585 if (SSpace
== string::npos
)
588 string Word1
= string(*I
,Space
,SSpace
-Space
);
589 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
592 string::size_type Space2
= (*J
).find(' ');
593 if (Space2
== string::npos
)
595 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
596 if (SSpace2
== string::npos
)
599 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
602 *J
+= string(*I
,SSpace
);
607 // Wipe erased entries
608 for (unsigned int I
= 0; I
< List
.size();)
610 if (List
[I
].empty() == false)
613 List
.erase(List
.begin()+I
);
617 // WriteDatabase - Write the CDROM Database file /*{{{*/
618 // ---------------------------------------------------------------------
619 /* We rewrite the configuration class associated with the cdrom database. */
620 bool WriteDatabase(Configuration
&Cnf
)
622 string DFile
= _config
->FindFile("Dir::State::cdroms");
623 string NewFile
= DFile
+ ".new";
625 unlink(NewFile
.c_str());
626 ofstream
Out(NewFile
.c_str());
628 return _error
->Errno("ofstream::ofstream",
629 "Failed to open %s.new",DFile
.c_str());
631 /* Write out all of the configuration directives by walking the
632 configuration tree */
633 const Configuration::Item
*Top
= Cnf
.Tree(0);
636 // Print the config entry
637 if (Top
->Value
.empty() == false)
638 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
646 while (Top
!= 0 && Top
->Next
== 0)
654 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
655 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
656 return _error
->Errno("rename","Failed to rename %s.new to %s",
657 DFile
.c_str(),DFile
.c_str());
662 // WriteSourceList - Write an updated sourcelist /*{{{*/
663 // ---------------------------------------------------------------------
664 /* This reads the old source list and copies it into the new one. It
665 appends the new CDROM entires just after the first block of comments.
666 This places them first in the file. It also removes any old entries
667 that were the same. */
668 bool WriteSourceList(string Name
,vector
<string
> &List
)
670 string File
= _config
->FindFile("Dir::Etc::sourcelist");
672 // Open the stream for reading
673 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
675 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
677 string NewFile
= File
+ ".new";
678 unlink(NewFile
.c_str());
679 ofstream
Out(NewFile
.c_str());
681 return _error
->Errno("ofstream::ofstream",
682 "Failed to open %s.new",File
.c_str());
684 // Create a short uri without the path
685 string ShortURI
= "cdrom:" + Name
+ "/";
690 while (F
.eof() == false)
692 F
.getline(Buffer
,sizeof(Buffer
));
694 _strtabexpand(Buffer
,sizeof(Buffer
));
698 if (Buffer
[0] == '#' || Buffer
[0] == 0)
700 Out
<< Buffer
<< endl
;
706 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
708 string::size_type Space
= (*I
).find(' ');
709 if (Space
== string::npos
)
710 return _error
->Error("Internal error");
712 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
713 "\" " << string(*I
,Space
+1) << endl
;
722 if (ParseQuoteWord(C
,Type
) == false ||
723 ParseQuoteWord(C
,URI
) == false)
725 Out
<< Buffer
<< endl
;
729 // Emit lines like this one
730 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
732 Out
<< Buffer
<< endl
;
737 // Just in case the file was empty
740 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
742 string::size_type Space
= (*I
).find(' ');
743 if (Space
== string::npos
)
744 return _error
->Error("Internal error");
746 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
747 "\" " << string(*I
,Space
+1) << endl
;
753 rename(File
.c_str(),string(File
+ '~').c_str());
754 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
755 return _error
->Errno("rename","Failed to rename %s.new to %s",
756 File
.c_str(),File
.c_str());
762 // Prompt - Simple prompt /*{{{*/
763 // ---------------------------------------------------------------------
765 void Prompt(const char *Text
)
768 cout
<< Text
<< ' ' << flush
;
769 read(STDIN_FILENO
,&C
,1);
774 // PromptLine - Prompt for an input line /*{{{*/
775 // ---------------------------------------------------------------------
777 string
PromptLine(const char *Text
)
779 cout
<< Text
<< ':' << endl
;
787 // DoAdd - Add a new CDROM /*{{{*/
788 // ---------------------------------------------------------------------
789 /* This does the main add bit.. We show some status and things. The
790 sequence is to mount/umount the CD, Ident it then scan it for package
791 files and reduce that list. Then we copy over the package files and
792 verify them. Then rewrite the database files */
793 bool DoAdd(CommandLine
&)
796 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
798 CDROM
= SafeGetCWD() + '/' + CDROM
;
800 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
803 Configuration Database
;
804 string DFile
= _config
->FindFile("Dir::State::cdroms");
805 if (FileExists(DFile
) == true)
807 if (ReadConfigFile(Database
,DFile
) == false)
808 return _error
->Error("Unable to read the cdrom database %s",
812 // Unmount the CD and get the user to put in the one they want
813 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
815 cout
<< "Unmounting CD-ROM" << endl
;
818 // Mount the new CDROM
819 Prompt("Please insert a Disc in the drive and press any key");
820 cout
<< "Mounting CD-ROM" << endl
;
821 if (MountCdrom(CDROM
) == false)
823 cout
<< "Failed to mount the cdrom." << endl
;
828 // Hash the CD to get an ID
829 cout
<< "Identifying.. " << flush
;
831 if (IdentCdrom(CDROM
,ID
) == false)
837 cout
<< '[' << ID
<< ']' << endl
;
839 cout
<< "Scanning Disc for index files.. " << flush
;
840 // Get the CD structure
842 string StartDir
= SafeGetCWD();
843 if (FindPackages(CDROM
,List
) == false)
849 chdir(StartDir
.c_str());
851 if (_config
->FindB("Debug::aptcdrom",false) == true)
853 cout
<< "I found:" << endl
;
854 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
861 DropBinaryArch(List
);
863 cout
<< "Found " << List
.size() << " package index files." << endl
;
865 if (List
.size() == 0)
866 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
868 // Check if the CD is in the database
870 if (Database
.Exists("CD::" + ID
) == false ||
871 _config
->FindB("APT::CDROM::Rename",false) == true)
873 // Try to use the CDs label if at all possible
874 if (FileExists(CDROM
+ "/.disk/info") == true)
876 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
880 if (Name
.empty() == false)
882 cout
<< "Found label '" << Name
<< "'" << endl
;
883 Database
.Set("CD::" + ID
+ "::Label",Name
);
887 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
888 Name
.empty() == true)
890 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
893 Name
= PromptLine("");
894 if (Name
.empty() == false &&
895 Name
.find('/') == string::npos
)
897 cout
<< "That is not a valid name, try again " << endl
;
903 Name
= Database
.Find("CD::" + ID
);
904 Database
.Set("CD::" + ID
,Name
);
905 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
907 // Copy the package files to the state directory
908 if (CopyPackages(CDROM
,Name
,List
) == false)
911 ReduceSourcelist(CDROM
,List
);
913 // Write the database and sourcelist
914 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
916 if (WriteDatabase(Database
) == false)
919 cout
<< "Writing new source list" << endl
;
920 if (WriteSourceList(Name
,List
) == false)
924 // Print the sourcelist entries
925 cout
<< "Source List entires for this Disc are:" << endl
;
926 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
928 string::size_type Space
= (*I
).find(' ');
929 if (Space
== string::npos
)
930 return _error
->Error("Internal error");
932 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
933 "\" " << string(*I
,Space
+1) << endl
;
940 // ShowHelp - Show the help screen /*{{{*/
941 // ---------------------------------------------------------------------
945 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
946 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
948 cout
<< "Usage: apt-cdrom [options] command" << endl
;
950 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
951 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
952 cout
<< "and /etc/fstab." << endl
;
954 cout
<< "Commands:" << endl
;
955 cout
<< " add - Add a CDROM" << endl
;
957 cout
<< "Options:" << endl
;
958 cout
<< " -h This help text" << endl
;
959 cout
<< " -d CD-ROM mount point" << endl
;
960 cout
<< " -r Rename a recognized CD-ROM" << endl
;
961 cout
<< " -m No mounting" << endl
;
962 cout
<< " -f Fast mode, don't check package files" << endl
;
963 cout
<< " -c=? Read this configuration file" << endl
;
964 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
965 cout
<< "See fstab(5)" << endl
;
970 int main(int argc
,const char *argv
[])
972 CommandLine::Args Args
[] = {
973 {'h',"help","help",0},
974 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
975 {'r',"rename","APT::CDROM::Rename",0},
976 {'m',"no-mount","APT::CDROM::NoMount",0},
977 {'f',"fast","APT::CDROM::Fast",0},
978 {'n',"just-print","APT::CDROM::NoAct",0},
979 {'n',"recon","APT::CDROM::NoAct",0},
980 {'n',"no-act","APT::CDROM::NoAct",0},
981 {'a',"thorough","APT::CDROM::Thorough",0},
982 {'c',"config-file",0,CommandLine::ConfigFile
},
983 {'o',"option",0,CommandLine::ArbItem
},
985 CommandLine::Dispatch Cmds
[] = {
989 // Parse the command line and initialize the package library
990 CommandLine
CmdL(Args
,_config
);
991 if (pkgInitialize(*_config
) == false ||
992 CmdL
.Parse(argc
,argv
) == false)
994 _error
->DumpErrors();
998 // See if the help should be shown
999 if (_config
->FindB("help") == true ||
1000 CmdL
.FileSize() == 0)
1003 // Match the operation
1004 CmdL
.DispatchArg(Cmds
);
1006 // Print any errors or warnings found during parsing
1007 if (_error
->empty() == false)
1009 bool Errors
= _error
->PendingError();
1010 _error
->DumpErrors();
1011 return Errors
== true?100:0;