]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.7 1998/12/04 23:33:18 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)
63 DIR *D
= opendir(".");
65 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
67 // Run over the directory
68 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
71 if (strcmp(Dir
->d_name
,".") == 0 ||
72 strcmp(Dir
->d_name
,"..") == 0 ||
73 strcmp(Dir
->d_name
,"source") == 0 ||
74 strcmp(Dir
->d_name
,"experimental") == 0 ||
75 strcmp(Dir
->d_name
,"binary-all") == 0)
78 // See if the name is a sub directory
80 if (stat(Dir
->d_name
,&Buf
) != 0)
83 if (S_ISDIR(Buf
.st_mode
) == 0)
87 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
90 if (chdir(CD
.c_str()) != 0)
91 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
96 return !_error
->PendingError();
99 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
100 // ---------------------------------------------------------------------
101 /* Here we drop everything that is not this machines arch */
102 bool DropBinaryArch(vector
<string
> &List
)
105 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
107 for (unsigned int I
= 0; I
< List
.size(); I
++)
109 const char *Str
= List
[I
].c_str();
112 if ((Res
= strstr(Str
,"/binary-")) == 0)
116 if (strlen(Res
) < strlen(S
))
118 List
.erase(List
.begin() + I
);
123 // See if it is our arch
124 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
128 List
.erase(List
.begin() + I
);
135 // Score - We compute a 'score' for a path /*{{{*/
136 // ---------------------------------------------------------------------
137 /* Paths are scored based on how close they come to what I consider
138 normal. That is ones that have 'dist' 'stable' 'frozen' will score
139 higher than ones without. */
140 int Score(string Path
)
143 if (Path
.find("stable/") != string::npos
)
145 if (Path
.find("frozen/") != string::npos
)
147 if (Path
.find("/dists/") != string::npos
)
149 if (Path
.find("/main/") != string::npos
)
151 if (Path
.find("/contrib/") != string::npos
)
153 if (Path
.find("/non-free/") != string::npos
)
155 if (Path
.find("/non-US/") != string::npos
)
160 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
161 // ---------------------------------------------------------------------
162 /* Here we go and stat every file that we found and strip dup inodes. */
163 bool DropRepeats(vector
<string
> &List
)
165 // Get a list of all the inodes
166 ino_t
*Inodes
= new ino_t
[List
.size()];
167 for (unsigned int I
= 0; I
!= List
.size(); I
++)
170 if (stat(List
[I
].c_str(),&Buf
) != 0)
171 _error
->Errno("stat","Failed to stat %s",List
[I
].c_str());
172 Inodes
[I
] = Buf
.st_ino
;
176 for (unsigned int I
= 0; I
!= List
.size(); I
++)
178 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
181 if (Inodes
[J
] != Inodes
[I
])
184 // We score the two paths.. and erase one
185 int ScoreA
= Score(List
[I
]);
186 int ScoreB
= Score(List
[J
]);
197 // Wipe erased entries
198 for (unsigned int I
= 0; I
< List
.size();)
200 if (List
[I
].empty() == false)
203 List
.erase(List
.begin()+I
);
209 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
210 // ---------------------------------------------------------------------
211 /* We look for things in dists/ notation and convert them to
212 <dist> <component> form otherwise it is left alone. This also strips
214 void ConvertToSourceList(string CD
,string
&Path
)
217 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
219 // Strip the cdrom base path
220 Path
= string(Path
,CD
.length());
221 if (Path
.empty() == true)
224 // Too short to be a dists/ type
225 if (Path
.length() < strlen("dists/"))
229 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
233 string::size_type Slash
= strlen("dists/");
234 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
235 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
237 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
239 // Isolate the component
240 Slash
= Path
.find('/',Slash2
+1);
241 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
243 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
245 // Verify the trailing binar - bit
246 Slash2
= Path
.find('/',Slash
+ 1);
247 if (Slash
== string::npos
)
249 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
254 Path
= Dist
+ ' ' + Comp
;
257 // GrabFirst - Return the first Depth path components /*{{{*/
258 // ---------------------------------------------------------------------
260 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
262 string::size_type I
= 0;
265 I
= Path
.find('/',I
+1);
268 while (I
!= string::npos
&& Depth
!= 0);
270 if (I
== string::npos
)
273 To
= string(Path
,0,I
+1);
277 // ChopDirs - Chop off the leading directory components /*{{{*/
278 // ---------------------------------------------------------------------
280 string
ChopDirs(string Path
,unsigned int Depth
)
282 string::size_type I
= 0;
285 I
= Path
.find('/',I
+1);
288 while (I
!= string::npos
&& Depth
!= 0);
290 if (I
== string::npos
)
293 return string(Path
,I
+1);
296 // ReconstructPrefix - Fix strange prefixing /*{{{*/
297 // ---------------------------------------------------------------------
298 /* This prepends dir components from the path to the package files to
299 the path to the deb until it is found */
300 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
303 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
304 unsigned int Depth
= 1;
305 string MyPrefix
= Prefix
;
309 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
312 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
313 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
327 // ReconstructChop - Fixes bad source paths /*{{{*/
328 // ---------------------------------------------------------------------
329 /* This removes path components from the filename and prepends the location
330 of the package files until a file is found */
331 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
333 // Attempt to reconstruct the filename
334 unsigned long Depth
= 0;
338 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
340 File
= ChopDirs(File
,1);
342 if (File
.empty() == false)
356 // CopyPackages - Copy the package files from the CD /*{{{*/
357 // ---------------------------------------------------------------------
359 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
361 OpTextProgress Progress
;
363 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
364 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
366 // Prepare the progress indicator
367 unsigned long TotalSize
= 0;
368 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
371 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
372 return _error
->Errno("stat","Stat failed for %s",
373 string(*I
+ "Packages").c_str());
374 TotalSize
+= Buf
.st_size
;
377 unsigned long CurrentSize
= 0;
378 unsigned int NotFound
= 0;
379 unsigned int WrongSize
= 0;
380 unsigned int Packages
= 0;
381 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
383 string OrigPath
= string(*I
,CDROM
.length());
385 // Open the package file
386 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
387 pkgTagFile
Parser(Pkg
);
388 if (_error
->PendingError() == true)
391 // Open the output file
393 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
394 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
395 TargetF
+= URItoFileName(S
);
396 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
397 TargetF
= "/dev/null";
398 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
399 if (_error
->PendingError() == true)
402 // Setup the progress meter
403 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
404 "Reading Package Lists");
407 Progress
.SubProgress(Pkg
.Size());
408 pkgTagSection Section
;
410 unsigned long Hits
= 0;
411 unsigned long Chop
= 0;
412 while (Parser
.Step(Section
) == true)
414 Progress
.Progress(Parser
.Offset());
416 string File
= Section
.FindS("Filename");
417 unsigned long Size
= Section
.FindI("Size");
418 if (File
.empty() || Size
== 0)
419 return _error
->Error("Cannot find filename or size tag");
422 File
= OrigPath
+ ChopDirs(File
,Chop
);
424 // See if the file exists
425 if (NoStat
== false || Hits
< 10)
427 // Attempt to fix broken structure
430 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
431 ReconstructChop(Chop
,*I
,File
) == false)
437 File
= OrigPath
+ ChopDirs(File
,Chop
);
442 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
449 if ((unsigned)Buf
.st_size
!= Size
)
459 // Copy it to the target package file
464 // Mangle the output filename
465 const char *Filename
;
466 Section
.Find("Filename",Filename
,Stop
);
468 /* We need to rewrite the filename field so we emit
469 all fields except the filename file and rewrite that one */
470 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
472 Section
.Get(Start
,Stop
,I
);
473 if (Start
<= Filename
&& Stop
> Filename
)
476 sprintf(S
,"Filename: %s\n",File
.c_str());
477 if (I
+ 1 == Section
.Count())
479 if (Target
.Write(S
,strlen(S
)) == false)
483 if (Target
.Write(Start
,Stop
-Start
) == false)
486 if (Target
.Write("\n",1) == false)
491 Section
.GetSection(Start
,Stop
);
492 if (Target
.Write(Start
,Stop
-Start
) == false)
498 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
500 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
502 // Move out of the partial directory
504 string FinalF
= _config
->FindDir("Dir::State::lists");
505 FinalF
+= URItoFileName(S
);
506 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
507 return _error
->Errno("rename","Failed to rename");
509 // Copy the release file
510 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
511 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
512 TargetF
+= URItoFileName(S
);
513 if (FileExists(*I
+ "Release") == true)
515 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
516 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
517 if (_error
->PendingError() == true)
520 if (CopyFile(Rel
,Target
) == false)
525 // Empty release file
526 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
529 // Rename the release file
530 FinalF
= _config
->FindDir("Dir::State::lists");
531 FinalF
+= URItoFileName(S
);
532 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
533 return _error
->Errno("rename","Failed to rename");
536 /* Mangle the source to be in the proper notation with
537 prefix dist [component] */
538 *I
= string(*I
,Prefix
.length());
539 ConvertToSourceList(CDROM
,*I
);
540 *I
= Prefix
+ ' ' + *I
;
542 CurrentSize
+= Pkg
.Size();
547 cout
<< "Wrote " << Packages
<< " package records" ;
549 cout
<< " with " << NotFound
<< " missing files";
550 if (NotFound
!= 0 && WrongSize
!= 0)
553 cout
<< " with " << WrongSize
<< " mismatched files";
557 return _error
->Error("No valid package records were found.");
559 if (NotFound
+ WrongSize
> 10)
560 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
566 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
567 // ---------------------------------------------------------------------
568 /* This takes the list of source list expressed entires and collects
569 similar ones to form a single entry for each dist */
570 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
572 sort(List
.begin(),List
.end());
574 // Collect similar entries
575 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
578 string::size_type Space
= (*I
).find(' ');
579 if (Space
== string::npos
)
581 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
582 if (SSpace
== string::npos
)
585 string Word1
= string(*I
,Space
,SSpace
-Space
);
586 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
589 string::size_type Space2
= (*J
).find(' ');
590 if (Space2
== string::npos
)
592 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
593 if (SSpace2
== string::npos
)
596 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
599 *J
+= string(*I
,SSpace
);
604 // Wipe erased entries
605 for (unsigned int I
= 0; I
< List
.size();)
607 if (List
[I
].empty() == false)
610 List
.erase(List
.begin()+I
);
614 // WriteDatabase - Write the CDROM Database file /*{{{*/
615 // ---------------------------------------------------------------------
616 /* We rewrite the configuration class associated with the cdrom database. */
617 bool WriteDatabase(Configuration
&Cnf
)
619 string DFile
= _config
->FindFile("Dir::State::cdroms");
620 string NewFile
= DFile
+ ".new";
622 unlink(NewFile
.c_str());
623 ofstream
Out(NewFile
.c_str());
625 return _error
->Errno("ofstream::ofstream",
626 "Failed to open %s.new",DFile
.c_str());
628 /* Write out all of the configuration directives by walking the
629 configuration tree */
630 const Configuration::Item
*Top
= Cnf
.Tree(0);
633 // Print the config entry
634 if (Top
->Value
.empty() == false)
635 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
643 while (Top
!= 0 && Top
->Next
== 0)
651 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
652 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
653 return _error
->Errno("rename","Failed to rename %s.new to %s",
654 DFile
.c_str(),DFile
.c_str());
659 // WriteSourceList - Write an updated sourcelist /*{{{*/
660 // ---------------------------------------------------------------------
661 /* This reads the old source list and copies it into the new one. It
662 appends the new CDROM entires just after the first block of comments.
663 This places them first in the file. It also removes any old entries
664 that were the same. */
665 bool WriteSourceList(string Name
,vector
<string
> &List
)
667 string File
= _config
->FindFile("Dir::Etc::sourcelist");
669 // Open the stream for reading
670 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
672 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
674 string NewFile
= File
+ ".new";
675 unlink(NewFile
.c_str());
676 ofstream
Out(NewFile
.c_str());
678 return _error
->Errno("ofstream::ofstream",
679 "Failed to open %s.new",File
.c_str());
681 // Create a short uri without the path
682 string ShortURI
= "cdrom:" + Name
+ "/";
687 while (F
.eof() == false)
689 F
.getline(Buffer
,sizeof(Buffer
));
691 _strtabexpand(Buffer
,sizeof(Buffer
));
695 if (Buffer
[0] == '#' || Buffer
[0] == 0)
697 Out
<< Buffer
<< endl
;
703 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
705 string::size_type Space
= (*I
).find(' ');
706 if (Space
== string::npos
)
707 return _error
->Error("Internal error");
709 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
710 "\" " << string(*I
,Space
+1) << endl
;
719 if (ParseQuoteWord(C
,Type
) == false ||
720 ParseQuoteWord(C
,URI
) == false)
722 Out
<< Buffer
<< endl
;
726 // Emit lines like this one
727 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
729 Out
<< Buffer
<< endl
;
734 // Just in case the file was empty
737 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
739 string::size_type Space
= (*I
).find(' ');
740 if (Space
== string::npos
)
741 return _error
->Error("Internal error");
743 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
744 "\" " << string(*I
,Space
+1) << endl
;
750 rename(File
.c_str(),string(File
+ '~').c_str());
751 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
752 return _error
->Errno("rename","Failed to rename %s.new to %s",
753 File
.c_str(),File
.c_str());
759 // Prompt - Simple prompt /*{{{*/
760 // ---------------------------------------------------------------------
762 void Prompt(const char *Text
)
765 cout
<< Text
<< ' ' << flush
;
766 read(STDIN_FILENO
,&C
,1);
771 // PromptLine - Prompt for an input line /*{{{*/
772 // ---------------------------------------------------------------------
774 string
PromptLine(const char *Text
)
776 cout
<< Text
<< ':' << endl
;
784 // DoAdd - Add a new CDROM /*{{{*/
785 // ---------------------------------------------------------------------
786 /* This does the main add bit.. We show some status and things. The
787 sequence is to mount/umount the CD, Ident it then scan it for package
788 files and reduce that list. Then we copy over the package files and
789 verify them. Then rewrite the database files */
790 bool DoAdd(CommandLine
&)
793 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
795 CDROM
= SafeGetCWD() + '/' + CDROM
;
797 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
800 Configuration Database
;
801 string DFile
= _config
->FindFile("Dir::State::cdroms");
802 if (FileExists(DFile
) == true)
804 if (ReadConfigFile(Database
,DFile
) == false)
805 return _error
->Error("Unable to read the cdrom database %s",
809 // Unmount the CD and get the user to put in the one they want
810 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
812 cout
<< "Unmounting CD-ROM" << endl
;
815 // Mount the new CDROM
816 Prompt("Please insert a Disc in the drive and press any key");
817 cout
<< "Mounting CD-ROM" << endl
;
818 if (MountCdrom(CDROM
) == false)
820 cout
<< "Failed to mount the cdrom." << endl
;
825 // Hash the CD to get an ID
826 cout
<< "Identifying.. " << flush
;
828 if (IdentCdrom(CDROM
,ID
) == false)
834 cout
<< '[' << ID
<< ']' << endl
;
836 cout
<< "Scanning Disc for index files.. " << flush
;
837 // Get the CD structure
839 string StartDir
= SafeGetCWD();
840 if (FindPackages(CDROM
,List
) == false)
846 chdir(StartDir
.c_str());
848 if (_config
->FindB("Debug::aptcdrom",false) == true)
850 cout
<< "I found:" << endl
;
851 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
858 DropBinaryArch(List
);
860 cout
<< "Found " << List
.size() << " package index files." << endl
;
862 if (List
.size() == 0)
863 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
865 // Check if the CD is in the database
867 if (Database
.Exists("CD::" + ID
) == false ||
868 _config
->FindB("APT::CDROM::Rename",false) == true)
870 // Try to use the CDs label if at all possible
871 if (FileExists(CDROM
+ "/.disk/info") == true)
873 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
877 if (Name
.empty() == false)
879 cout
<< "Found label '" << Name
<< "'" << endl
;
880 Database
.Set("CD::" + ID
+ "::Label",Name
);
884 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
885 Name
.empty() == true)
887 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
890 Name
= PromptLine("");
891 if (Name
.empty() == false &&
892 Name
.find('/') == string::npos
)
894 cout
<< "That is not a valid name, try again " << endl
;
900 Name
= Database
.Find("CD::" + ID
);
901 Database
.Set("CD::" + ID
,Name
);
902 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
904 // Copy the package files to the state directory
905 if (CopyPackages(CDROM
,Name
,List
) == false)
908 ReduceSourcelist(CDROM
,List
);
910 // Write the database and sourcelist
911 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
913 if (WriteDatabase(Database
) == false)
916 cout
<< "Writing new source list" << endl
;
917 if (WriteSourceList(Name
,List
) == false)
921 // Print the sourcelist entries
922 cout
<< "Source List entires for this Disc are:" << endl
;
923 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
925 string::size_type Space
= (*I
).find(' ');
926 if (Space
== string::npos
)
927 return _error
->Error("Internal error");
929 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
930 "\" " << string(*I
,Space
+1) << endl
;
937 // ShowHelp - Show the help screen /*{{{*/
938 // ---------------------------------------------------------------------
942 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
943 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
945 cout
<< "Usage: apt-cdrom [options] command" << endl
;
947 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
948 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
949 cout
<< "and /etc/fstab." << endl
;
951 cout
<< "Commands:" << endl
;
952 cout
<< " add - Add a CDROM" << endl
;
954 cout
<< "Options:" << endl
;
955 cout
<< " -h This help text" << endl
;
956 cout
<< " -d CD-ROM mount point" << endl
;
957 cout
<< " -r Rename a recognized CD-ROM" << endl
;
958 cout
<< " -m No mounting" << endl
;
959 cout
<< " -f Fast mode, don't check package files" << endl
;
960 cout
<< " -c=? Read this configuration file" << endl
;
961 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
962 cout
<< "See fstab(5)" << endl
;
967 int main(int argc
,const char *argv
[])
969 CommandLine::Args Args
[] = {
970 {'h',"help","help",0},
971 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
972 {'r',"rename","APT::CDROM::Rename",0},
973 {'m',"no-mount","APT::CDROM::NoMount",0},
974 {'f',"fast","APT::CDROM::Fast",0},
975 {'n',"just-print","APT::CDROM::NoAct",0},
976 {'n',"recon","APT::CDROM::NoAct",0},
977 {'n',"no-act","APT::CDROM::NoAct",0},
978 {'c',"config-file",0,CommandLine::ConfigFile
},
979 {'o',"option",0,CommandLine::ArbItem
},
981 CommandLine::Dispatch Cmds
[] = {
985 // Parse the command line and initialize the package library
986 CommandLine
CmdL(Args
,_config
);
987 if (pkgInitialize(*_config
) == false ||
988 CmdL
.Parse(argc
,argv
) == false)
990 _error
->DumpErrors();
994 // See if the help should be shown
995 if (_config
->FindB("help") == true ||
996 CmdL
.FileSize() == 0)
999 // Match the operation
1000 CmdL
.DispatchArg(Cmds
);
1002 // Print any errors or warnings found during parsing
1003 if (_error
->empty() == false)
1005 bool Errors
= _error
->PendingError();
1006 _error
->DumpErrors();
1007 return Errors
== true?100:0;