]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
dc4cc3cd6197a957116b34b414d56ac2323ee226
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.10 1998/12/09 00:31:16 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
43 bool FindPackages(string CD
,vector
<string
> &List
, unsigned int Depth
= 0)
48 if (CD
[CD
.length()-1] != '/')
51 if (chdir(CD
.c_str()) != 0)
52 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
54 /* Aha! We found some package files. We assume that everything under
55 this dir is controlled by those package files so we don't look down
58 if (stat("Packages",&Buf
) == 0 ||
59 stat("Packages.gz",&Buf
) == 0)
63 // Continue down if thorough is given
64 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
68 DIR *D
= opendir(".");
70 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
72 // Run over the directory
73 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
76 if (strcmp(Dir
->d_name
,".") == 0 ||
77 strcmp(Dir
->d_name
,"..") == 0 ||
78 strcmp(Dir
->d_name
,"source") == 0 ||
79 strcmp(Dir
->d_name
,"experimental") == 0 ||
80 strcmp(Dir
->d_name
,"binary-all") == 0)
83 // See if the name is a sub directory
85 if (stat(Dir
->d_name
,&Buf
) != 0)
88 if (S_ISDIR(Buf
.st_mode
) == 0)
92 for (I
= 0; I
!= Depth
; I
++)
93 if (Inodes
[I
] == Buf
.st_ino
)
95 if (Inodes
[I
] == Buf
.st_ino
)
98 // Store the inodes weve seen
99 Inodes
[Depth
] = Buf
.st_ino
;
102 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
105 if (chdir(CD
.c_str()) != 0)
106 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
111 return !_error
->PendingError();
114 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
115 // ---------------------------------------------------------------------
116 /* Here we drop everything that is not this machines arch */
117 bool DropBinaryArch(vector
<string
> &List
)
120 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
122 for (unsigned int I
= 0; I
< List
.size(); I
++)
124 const char *Str
= List
[I
].c_str();
127 if ((Res
= strstr(Str
,"/binary-")) == 0)
131 if (strlen(Res
) < strlen(S
))
133 List
.erase(List
.begin() + I
);
138 // See if it is our arch
139 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
143 List
.erase(List
.begin() + I
);
150 // Score - We compute a 'score' for a path /*{{{*/
151 // ---------------------------------------------------------------------
152 /* Paths are scored based on how close they come to what I consider
153 normal. That is ones that have 'dist' 'stable' 'frozen' will score
154 higher than ones without. */
155 int Score(string Path
)
158 if (Path
.find("stable/") != string::npos
)
160 if (Path
.find("frozen/") != string::npos
)
162 if (Path
.find("/dists/") != string::npos
)
164 if (Path
.find("/main/") != string::npos
)
166 if (Path
.find("/contrib/") != string::npos
)
168 if (Path
.find("/non-free/") != string::npos
)
170 if (Path
.find("/non-US/") != string::npos
)
175 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
176 // ---------------------------------------------------------------------
177 /* Here we go and stat every file that we found and strip dup inodes. */
178 bool DropRepeats(vector
<string
> &List
)
180 // Get a list of all the inodes
181 ino_t
*Inodes
= new ino_t
[List
.size()];
182 for (unsigned int I
= 0; I
!= List
.size(); I
++)
185 if (stat(List
[I
].c_str(),&Buf
) != 0)
186 _error
->Errno("stat","Failed to stat %s",List
[I
].c_str());
187 Inodes
[I
] = Buf
.st_ino
;
191 for (unsigned int I
= 0; I
!= List
.size(); I
++)
193 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
196 if (Inodes
[J
] != Inodes
[I
])
199 // We score the two paths.. and erase one
200 int ScoreA
= Score(List
[I
]);
201 int ScoreB
= Score(List
[J
]);
212 // Wipe erased entries
213 for (unsigned int I
= 0; I
< List
.size();)
215 if (List
[I
].empty() == false)
218 List
.erase(List
.begin()+I
);
224 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
225 // ---------------------------------------------------------------------
226 /* We look for things in dists/ notation and convert them to
227 <dist> <component> form otherwise it is left alone. This also strips
229 void ConvertToSourceList(string CD
,string
&Path
)
232 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
234 // Strip the cdrom base path
235 Path
= string(Path
,CD
.length());
236 if (Path
.empty() == true)
239 // Too short to be a dists/ type
240 if (Path
.length() < strlen("dists/"))
244 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
248 string::size_type Slash
= strlen("dists/");
249 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
250 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
252 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
254 // Isolate the component
255 Slash
= Path
.find('/',Slash2
+1);
256 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
258 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
260 // Verify the trailing binar - bit
261 Slash2
= Path
.find('/',Slash
+ 1);
262 if (Slash
== string::npos
)
264 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
269 Path
= Dist
+ ' ' + Comp
;
272 // GrabFirst - Return the first Depth path components /*{{{*/
273 // ---------------------------------------------------------------------
275 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
277 string::size_type I
= 0;
280 I
= Path
.find('/',I
+1);
283 while (I
!= string::npos
&& Depth
!= 0);
285 if (I
== string::npos
)
288 To
= string(Path
,0,I
+1);
292 // ChopDirs - Chop off the leading directory components /*{{{*/
293 // ---------------------------------------------------------------------
295 string
ChopDirs(string Path
,unsigned int Depth
)
297 string::size_type I
= 0;
300 I
= Path
.find('/',I
+1);
303 while (I
!= string::npos
&& Depth
!= 0);
305 if (I
== string::npos
)
308 return string(Path
,I
+1);
311 // ReconstructPrefix - Fix strange prefixing /*{{{*/
312 // ---------------------------------------------------------------------
313 /* This prepends dir components from the path to the package files to
314 the path to the deb until it is found */
315 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
318 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
319 unsigned int Depth
= 1;
320 string MyPrefix
= Prefix
;
324 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
327 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
328 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
342 // ReconstructChop - Fixes bad source paths /*{{{*/
343 // ---------------------------------------------------------------------
344 /* This removes path components from the filename and prepends the location
345 of the package files until a file is found */
346 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
348 // Attempt to reconstruct the filename
349 unsigned long Depth
= 0;
353 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
355 File
= ChopDirs(File
,1);
357 if (File
.empty() == false)
371 // CopyPackages - Copy the package files from the CD /*{{{*/
372 // ---------------------------------------------------------------------
374 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
376 OpTextProgress Progress
;
378 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
379 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
381 // Prepare the progress indicator
382 unsigned long TotalSize
= 0;
383 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
386 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
387 return _error
->Errno("stat","Stat failed for %s",
388 string(*I
+ "Packages").c_str());
389 TotalSize
+= Buf
.st_size
;
392 unsigned long CurrentSize
= 0;
393 unsigned int NotFound
= 0;
394 unsigned int WrongSize
= 0;
395 unsigned int Packages
= 0;
396 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
398 string OrigPath
= string(*I
,CDROM
.length());
400 // Open the package file
401 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
402 pkgTagFile
Parser(Pkg
);
403 if (_error
->PendingError() == true)
406 // Open the output file
408 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
409 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
410 TargetF
+= URItoFileName(S
);
411 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
412 TargetF
= "/dev/null";
413 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
414 if (_error
->PendingError() == true)
417 // Setup the progress meter
418 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
419 "Reading Package Lists");
422 Progress
.SubProgress(Pkg
.Size());
423 pkgTagSection Section
;
425 unsigned long Hits
= 0;
426 unsigned long Chop
= 0;
427 while (Parser
.Step(Section
) == true)
429 Progress
.Progress(Parser
.Offset());
431 string File
= Section
.FindS("Filename");
432 unsigned long Size
= Section
.FindI("Size");
433 if (File
.empty() || Size
== 0)
434 return _error
->Error("Cannot find filename or size tag");
437 File
= OrigPath
+ ChopDirs(File
,Chop
);
439 // See if the file exists
440 if (NoStat
== false || Hits
< 10)
442 // Attempt to fix broken structure
445 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
446 ReconstructChop(Chop
,*I
,File
) == false)
452 File
= OrigPath
+ ChopDirs(File
,Chop
);
457 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
464 if ((unsigned)Buf
.st_size
!= Size
)
474 // Copy it to the target package file
479 // Mangle the output filename
480 const char *Filename
;
481 Section
.Find("Filename",Filename
,Stop
);
483 /* We need to rewrite the filename field so we emit
484 all fields except the filename file and rewrite that one */
485 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
487 Section
.Get(Start
,Stop
,I
);
488 if (Start
<= Filename
&& Stop
> Filename
)
491 sprintf(S
,"Filename: %s\n",File
.c_str());
492 if (I
+ 1 == Section
.Count())
494 if (Target
.Write(S
,strlen(S
)) == false)
498 if (Target
.Write(Start
,Stop
-Start
) == false)
501 if (Target
.Write("\n",1) == false)
506 Section
.GetSection(Start
,Stop
);
507 if (Target
.Write(Start
,Stop
-Start
) == false)
513 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
515 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
517 // Move out of the partial directory
519 string FinalF
= _config
->FindDir("Dir::State::lists");
520 FinalF
+= URItoFileName(S
);
521 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
522 return _error
->Errno("rename","Failed to rename");
524 // Copy the release file
525 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
526 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
527 TargetF
+= URItoFileName(S
);
528 if (FileExists(*I
+ "Release") == true)
530 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
531 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
532 if (_error
->PendingError() == true)
535 if (CopyFile(Rel
,Target
) == false)
540 // Empty release file
541 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
544 // Rename the release file
545 FinalF
= _config
->FindDir("Dir::State::lists");
546 FinalF
+= URItoFileName(S
);
547 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
548 return _error
->Errno("rename","Failed to rename");
551 /* Mangle the source to be in the proper notation with
552 prefix dist [component] */
553 *I
= string(*I
,Prefix
.length());
554 ConvertToSourceList(CDROM
,*I
);
555 *I
= Prefix
+ ' ' + *I
;
557 CurrentSize
+= Pkg
.Size();
562 cout
<< "Wrote " << Packages
<< " package records" ;
564 cout
<< " with " << NotFound
<< " missing files";
565 if (NotFound
!= 0 && WrongSize
!= 0)
568 cout
<< " with " << WrongSize
<< " mismatched files";
572 return _error
->Error("No valid package records were found.");
574 if (NotFound
+ WrongSize
> 10)
575 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
581 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
582 // ---------------------------------------------------------------------
583 /* This takes the list of source list expressed entires and collects
584 similar ones to form a single entry for each dist */
585 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
587 sort(List
.begin(),List
.end());
589 // Collect similar entries
590 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
593 string::size_type Space
= (*I
).find(' ');
594 if (Space
== string::npos
)
596 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
597 if (SSpace
== string::npos
)
600 string Word1
= string(*I
,Space
,SSpace
-Space
);
601 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
604 string::size_type Space2
= (*J
).find(' ');
605 if (Space2
== string::npos
)
607 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
608 if (SSpace2
== string::npos
)
611 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
614 *J
+= string(*I
,SSpace
);
619 // Wipe erased entries
620 for (unsigned int I
= 0; I
< List
.size();)
622 if (List
[I
].empty() == false)
625 List
.erase(List
.begin()+I
);
629 // WriteDatabase - Write the CDROM Database file /*{{{*/
630 // ---------------------------------------------------------------------
631 /* We rewrite the configuration class associated with the cdrom database. */
632 bool WriteDatabase(Configuration
&Cnf
)
634 string DFile
= _config
->FindFile("Dir::State::cdroms");
635 string NewFile
= DFile
+ ".new";
637 unlink(NewFile
.c_str());
638 ofstream
Out(NewFile
.c_str());
640 return _error
->Errno("ofstream::ofstream",
641 "Failed to open %s.new",DFile
.c_str());
643 /* Write out all of the configuration directives by walking the
644 configuration tree */
645 const Configuration::Item
*Top
= Cnf
.Tree(0);
648 // Print the config entry
649 if (Top
->Value
.empty() == false)
650 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
658 while (Top
!= 0 && Top
->Next
== 0)
666 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
667 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
668 return _error
->Errno("rename","Failed to rename %s.new to %s",
669 DFile
.c_str(),DFile
.c_str());
674 // WriteSourceList - Write an updated sourcelist /*{{{*/
675 // ---------------------------------------------------------------------
676 /* This reads the old source list and copies it into the new one. It
677 appends the new CDROM entires just after the first block of comments.
678 This places them first in the file. It also removes any old entries
679 that were the same. */
680 bool WriteSourceList(string Name
,vector
<string
> &List
)
682 string File
= _config
->FindFile("Dir::Etc::sourcelist");
684 // Open the stream for reading
685 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
687 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
689 string NewFile
= File
+ ".new";
690 unlink(NewFile
.c_str());
691 ofstream
Out(NewFile
.c_str());
693 return _error
->Errno("ofstream::ofstream",
694 "Failed to open %s.new",File
.c_str());
696 // Create a short uri without the path
697 string ShortURI
= "cdrom:" + Name
+ "/";
702 while (F
.eof() == false)
704 F
.getline(Buffer
,sizeof(Buffer
));
706 _strtabexpand(Buffer
,sizeof(Buffer
));
710 if (Buffer
[0] == '#' || Buffer
[0] == 0)
712 Out
<< Buffer
<< endl
;
718 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
720 string::size_type Space
= (*I
).find(' ');
721 if (Space
== string::npos
)
722 return _error
->Error("Internal error");
724 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
725 "\" " << string(*I
,Space
+1) << endl
;
734 if (ParseQuoteWord(C
,Type
) == false ||
735 ParseQuoteWord(C
,URI
) == false)
737 Out
<< Buffer
<< endl
;
741 // Emit lines like this one
742 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
744 Out
<< Buffer
<< endl
;
749 // Just in case the file was empty
752 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
754 string::size_type Space
= (*I
).find(' ');
755 if (Space
== string::npos
)
756 return _error
->Error("Internal error");
758 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
759 "\" " << string(*I
,Space
+1) << endl
;
765 rename(File
.c_str(),string(File
+ '~').c_str());
766 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
767 return _error
->Errno("rename","Failed to rename %s.new to %s",
768 File
.c_str(),File
.c_str());
774 // Prompt - Simple prompt /*{{{*/
775 // ---------------------------------------------------------------------
777 void Prompt(const char *Text
)
780 cout
<< Text
<< ' ' << flush
;
781 read(STDIN_FILENO
,&C
,1);
786 // PromptLine - Prompt for an input line /*{{{*/
787 // ---------------------------------------------------------------------
789 string
PromptLine(const char *Text
)
791 cout
<< Text
<< ':' << endl
;
799 // DoAdd - Add a new CDROM /*{{{*/
800 // ---------------------------------------------------------------------
801 /* This does the main add bit.. We show some status and things. The
802 sequence is to mount/umount the CD, Ident it then scan it for package
803 files and reduce that list. Then we copy over the package files and
804 verify them. Then rewrite the database files */
805 bool DoAdd(CommandLine
&)
808 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
810 CDROM
= SafeGetCWD() + '/' + 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)
849 cout
<< '[' << ID
<< ']' << endl
;
851 cout
<< "Scanning Disc for index files.. " << flush
;
852 // Get the CD structure
854 string StartDir
= SafeGetCWD();
855 if (FindPackages(CDROM
,List
) == false)
861 chdir(StartDir
.c_str());
863 if (_config
->FindB("Debug::aptcdrom",false) == true)
865 cout
<< "I found:" << endl
;
866 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
873 DropBinaryArch(List
);
875 cout
<< "Found " << List
.size() << " package index files." << endl
;
877 if (List
.size() == 0)
878 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
880 // Check if the CD is in the database
882 if (Database
.Exists("CD::" + ID
) == false ||
883 _config
->FindB("APT::CDROM::Rename",false) == true)
885 // Try to use the CDs label if at all possible
886 if (FileExists(CDROM
+ "/.disk/info") == true)
888 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
892 if (Name
.empty() == false)
894 cout
<< "Found label '" << Name
<< "'" << endl
;
895 Database
.Set("CD::" + ID
+ "::Label",Name
);
899 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
900 Name
.empty() == true)
902 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
905 Name
= PromptLine("");
906 if (Name
.empty() == false &&
907 Name
.find('/') == string::npos
)
909 cout
<< "That is not a valid name, try again " << endl
;
915 Name
= Database
.Find("CD::" + ID
);
916 Database
.Set("CD::" + ID
,Name
);
917 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
919 // Copy the package files to the state directory
920 if (CopyPackages(CDROM
,Name
,List
) == false)
923 ReduceSourcelist(CDROM
,List
);
925 // Write the database and sourcelist
926 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
928 if (WriteDatabase(Database
) == false)
931 cout
<< "Writing new source list" << endl
;
932 if (WriteSourceList(Name
,List
) == false)
936 // Print the sourcelist entries
937 cout
<< "Source List entires for this Disc are:" << endl
;
938 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
940 string::size_type Space
= (*I
).find(' ');
941 if (Space
== string::npos
)
942 return _error
->Error("Internal error");
944 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
945 "\" " << string(*I
,Space
+1) << endl
;
952 // ShowHelp - Show the help screen /*{{{*/
953 // ---------------------------------------------------------------------
957 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
958 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
960 cout
<< "Usage: apt-cdrom [options] command" << endl
;
962 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
963 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
964 cout
<< "and /etc/fstab." << endl
;
966 cout
<< "Commands:" << endl
;
967 cout
<< " add - Add a CDROM" << endl
;
969 cout
<< "Options:" << endl
;
970 cout
<< " -h This help text" << endl
;
971 cout
<< " -d CD-ROM mount point" << endl
;
972 cout
<< " -r Rename a recognized CD-ROM" << endl
;
973 cout
<< " -m No mounting" << endl
;
974 cout
<< " -f Fast mode, don't check package files" << endl
;
975 cout
<< " -a Thorough scan mode" << endl
;
976 cout
<< " -c=? Read this configuration file" << endl
;
977 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
978 cout
<< "See fstab(5)" << endl
;
983 int main(int argc
,const char *argv
[])
985 CommandLine::Args Args
[] = {
986 {'h',"help","help",0},
987 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
988 {'r',"rename","APT::CDROM::Rename",0},
989 {'m',"no-mount","APT::CDROM::NoMount",0},
990 {'f',"fast","APT::CDROM::Fast",0},
991 {'n',"just-print","APT::CDROM::NoAct",0},
992 {'n',"recon","APT::CDROM::NoAct",0},
993 {'n',"no-act","APT::CDROM::NoAct",0},
994 {'a',"thorough","APT::CDROM::Thorough",0},
995 {'c',"config-file",0,CommandLine::ConfigFile
},
996 {'o',"option",0,CommandLine::ArbItem
},
998 CommandLine::Dispatch Cmds
[] = {
1002 // Parse the command line and initialize the package library
1003 CommandLine
CmdL(Args
,_config
);
1004 if (pkgInitialize(*_config
) == false ||
1005 CmdL
.Parse(argc
,argv
) == false)
1007 _error
->DumpErrors();
1011 // See if the help should be shown
1012 if (_config
->FindB("help") == true ||
1013 CmdL
.FileSize() == 0)
1016 // Match the operation
1017 CmdL
.DispatchArg(Cmds
);
1019 // Print any errors or warnings found during parsing
1020 if (_error
->empty() == false)
1022 bool Errors
= _error
->PendingError();
1023 _error
->DumpErrors();
1024 return Errors
== true?100:0;