]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.18 1999/03/27 01:30:38 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>
21 #include <apt-pkg/strutl.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
, unsigned int Depth
= 0)
43 static ino_t Inodes
[9];
47 if (CD
[CD
.length()-1] != '/')
50 if (chdir(CD
.c_str()) != 0)
51 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
53 /* Aha! We found some package files. We assume that everything under
54 this dir is controlled by those package files so we don't look down
57 if (stat("Packages",&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 for (I
= 0; I
!= Depth
; I
++)
91 if (Inodes
[I
] == Buf
.st_ino
)
96 // Store the inodes weve seen
97 Inodes
[Depth
] = Buf
.st_ino
;
100 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
103 if (chdir(CD
.c_str()) != 0)
104 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
109 return !_error
->PendingError();
112 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
113 // ---------------------------------------------------------------------
114 /* Here we drop everything that is not this machines arch */
115 bool DropBinaryArch(vector
<string
> &List
)
118 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
120 for (unsigned int I
= 0; I
< List
.size(); I
++)
122 const char *Str
= List
[I
].c_str();
125 if ((Res
= strstr(Str
,"/binary-")) == 0)
129 if (strlen(Res
) < strlen(S
))
131 List
.erase(List
.begin() + I
);
136 // See if it is our arch
137 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
141 List
.erase(List
.begin() + I
);
148 // Score - We compute a 'score' for a path /*{{{*/
149 // ---------------------------------------------------------------------
150 /* Paths are scored based on how close they come to what I consider
151 normal. That is ones that have 'dist' 'stable' 'frozen' will score
152 higher than ones without. */
153 int Score(string Path
)
156 if (Path
.find("stable/") != string::npos
)
158 if (Path
.find("/binary-") != 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
] + "Packages").c_str(),&Buf
) != 0)
186 _error
->Errno("stat","Failed to stat %sPackages",List
[I
].c_str());
187 Inodes
[I
] = Buf
.st_ino
;
190 if (_error
->PendingError() == true)
194 for (unsigned int I
= 0; I
!= List
.size(); I
++)
196 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
199 if (Inodes
[J
] != Inodes
[I
])
202 // We score the two paths.. and erase one
203 int ScoreA
= Score(List
[I
]);
204 int ScoreB
= Score(List
[J
]);
215 // Wipe erased entries
216 for (unsigned int I
= 0; I
< List
.size();)
218 if (List
[I
].empty() == false)
221 List
.erase(List
.begin()+I
);
227 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
228 // ---------------------------------------------------------------------
229 /* We look for things in dists/ notation and convert them to
230 <dist> <component> form otherwise it is left alone. This also strips
232 void ConvertToSourceList(string CD
,string
&Path
)
235 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
237 // Strip the cdrom base path
238 Path
= string(Path
,CD
.length());
239 if (Path
.empty() == true)
242 // Too short to be a dists/ type
243 if (Path
.length() < strlen("dists/"))
247 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
251 string::size_type Slash
= strlen("dists/");
252 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
253 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
255 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
257 // Isolate the component
258 Slash
= Path
.find('/',Slash2
+1);
259 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
261 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
263 // Verify the trailing binar - bit
264 Slash2
= Path
.find('/',Slash
+ 1);
265 if (Slash
== string::npos
)
267 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
272 Path
= Dist
+ ' ' + Comp
;
275 // GrabFirst - Return the first Depth path components /*{{{*/
276 // ---------------------------------------------------------------------
278 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
280 string::size_type I
= 0;
283 I
= Path
.find('/',I
+1);
286 while (I
!= string::npos
&& Depth
!= 0);
288 if (I
== string::npos
)
291 To
= string(Path
,0,I
+1);
295 // ChopDirs - Chop off the leading directory components /*{{{*/
296 // ---------------------------------------------------------------------
298 string
ChopDirs(string Path
,unsigned int Depth
)
300 string::size_type I
= 0;
303 I
= Path
.find('/',I
+1);
306 while (I
!= string::npos
&& Depth
!= 0);
308 if (I
== string::npos
)
311 return string(Path
,I
+1);
314 // ReconstructPrefix - Fix strange prefixing /*{{{*/
315 // ---------------------------------------------------------------------
316 /* This prepends dir components from the path to the package files to
317 the path to the deb until it is found */
318 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
321 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
322 unsigned int Depth
= 1;
323 string MyPrefix
= Prefix
;
327 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
330 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
331 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
345 // ReconstructChop - Fixes bad source paths /*{{{*/
346 // ---------------------------------------------------------------------
347 /* This removes path components from the filename and prepends the location
348 of the package files until a file is found */
349 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
351 // Attempt to reconstruct the filename
352 unsigned long Depth
= 0;
356 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
358 File
= ChopDirs(File
,1);
360 if (File
.empty() == false)
374 // CopyPackages - Copy the package files from the CD /*{{{*/
375 // ---------------------------------------------------------------------
377 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
379 OpTextProgress Progress
;
381 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
382 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
384 // Prepare the progress indicator
385 unsigned long TotalSize
= 0;
386 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
389 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
390 return _error
->Errno("stat","Stat failed for %s",
391 string(*I
+ "Packages").c_str());
392 TotalSize
+= Buf
.st_size
;
395 unsigned long CurrentSize
= 0;
396 unsigned int NotFound
= 0;
397 unsigned int WrongSize
= 0;
398 unsigned int Packages
= 0;
399 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
401 string OrigPath
= string(*I
,CDROM
.length());
403 // Open the package file
404 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
405 pkgTagFile
Parser(Pkg
);
406 if (_error
->PendingError() == true)
409 // Open the output file
411 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
412 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
413 TargetF
+= URItoFileName(S
);
414 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
415 TargetF
= "/dev/null";
416 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
417 if (_error
->PendingError() == true)
420 // Setup the progress meter
421 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
422 "Reading Package Lists");
425 Progress
.SubProgress(Pkg
.Size());
426 pkgTagSection Section
;
428 unsigned long Hits
= 0;
429 unsigned long Chop
= 0;
430 while (Parser
.Step(Section
) == true)
432 Progress
.Progress(Parser
.Offset());
434 string File
= Section
.FindS("Filename");
435 unsigned long Size
= Section
.FindI("Size");
436 if (File
.empty() || Size
== 0)
437 return _error
->Error("Cannot find filename or size tag");
440 File
= OrigPath
+ ChopDirs(File
,Chop
);
442 // See if the file exists
443 if (NoStat
== false || Hits
< 10)
445 // Attempt to fix broken structure
448 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
449 ReconstructChop(Chop
,*I
,File
) == false)
455 File
= OrigPath
+ ChopDirs(File
,Chop
);
460 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
467 if ((unsigned)Buf
.st_size
!= Size
)
477 // Copy it to the target package file
482 // Mangle the output filename
483 const char *Filename
;
484 Section
.Find("Filename",Filename
,Stop
);
486 /* We need to rewrite the filename field so we emit
487 all fields except the filename file and rewrite that one */
488 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
490 Section
.Get(Start
,Stop
,I
);
491 if (Start
<= Filename
&& Stop
> Filename
)
494 sprintf(S
,"Filename: %s\n",File
.c_str());
495 if (I
+ 1 == Section
.Count())
497 if (Target
.Write(S
,strlen(S
)) == false)
501 if (Target
.Write(Start
,Stop
-Start
) == false)
504 if (Target
.Write("\n",1) == false)
509 Section
.GetSection(Start
,Stop
);
510 if (Target
.Write(Start
,Stop
-Start
) == false)
516 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
518 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
520 // Move out of the partial directory
522 string FinalF
= _config
->FindDir("Dir::State::lists");
523 FinalF
+= URItoFileName(S
);
524 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
525 return _error
->Errno("rename","Failed to rename");
527 // Copy the release file
528 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
529 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
530 TargetF
+= URItoFileName(S
);
531 if (FileExists(*I
+ "Release") == true)
533 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
534 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
535 if (_error
->PendingError() == true)
538 if (CopyFile(Rel
,Target
) == false)
543 // Empty release file
544 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
547 // Rename the release file
548 FinalF
= _config
->FindDir("Dir::State::lists");
549 FinalF
+= URItoFileName(S
);
550 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
551 return _error
->Errno("rename","Failed to rename");
554 /* Mangle the source to be in the proper notation with
555 prefix dist [component] */
556 *I
= string(*I
,Prefix
.length());
557 ConvertToSourceList(CDROM
,*I
);
558 *I
= Prefix
+ ' ' + *I
;
560 CurrentSize
+= Pkg
.Size();
565 cout
<< "Wrote " << Packages
<< " package records" ;
567 cout
<< " with " << NotFound
<< " missing files";
568 if (NotFound
!= 0 && WrongSize
!= 0)
571 cout
<< " with " << WrongSize
<< " mismatched files";
575 return _error
->Error("No valid package records were found.");
577 if (NotFound
+ WrongSize
> 10)
578 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/");
813 CDROM
= SafeGetCWD() + '/' + CDROM
;
815 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
818 Configuration Database
;
819 string DFile
= _config
->FindFile("Dir::State::cdroms");
820 if (FileExists(DFile
) == true)
822 if (ReadConfigFile(Database
,DFile
) == false)
823 return _error
->Error("Unable to read the cdrom database %s",
827 // Unmount the CD and get the user to put in the one they want
828 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
830 cout
<< "Unmounting CD-ROM" << endl
;
833 // Mount the new CDROM
834 Prompt("Please insert a Disc in the drive and press any key");
835 cout
<< "Mounting CD-ROM" << endl
;
836 if (MountCdrom(CDROM
) == false)
838 cout
<< "Failed to mount the cdrom." << endl
;
843 // Hash the CD to get an ID
844 cout
<< "Identifying.. " << flush
;
846 if (IdentCdrom(CDROM
,ID
) == false)
852 cout
<< '[' << ID
<< ']' << endl
;
854 cout
<< "Scanning Disc for index files.. " << flush
;
855 // Get the CD structure
857 string StartDir
= SafeGetCWD();
858 if (FindPackages(CDROM
,List
) == false)
864 chdir(StartDir
.c_str());
866 if (_config
->FindB("Debug::aptcdrom",false) == true)
868 cout
<< "I found:" << endl
;
869 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
876 DropBinaryArch(List
);
878 cout
<< "Found " << List
.size() << " package index files." << endl
;
880 if (List
.size() == 0)
881 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
883 // Check if the CD is in the database
885 if (Database
.Exists("CD::" + ID
) == false ||
886 _config
->FindB("APT::CDROM::Rename",false) == true)
888 // Try to use the CDs label if at all possible
889 if (FileExists(CDROM
+ "/.disk/info") == true)
891 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
895 if (Name
.empty() == false)
897 cout
<< "Found label '" << Name
<< "'" << endl
;
898 Database
.Set("CD::" + ID
+ "::Label",Name
);
902 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
903 Name
.empty() == true)
905 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
908 Name
= PromptLine("");
909 if (Name
.empty() == false &&
910 Name
.find('"') == string::npos
&&
911 Name
.find(':') == string::npos
&&
912 Name
.find('/') == string::npos
)
914 cout
<< "That is not a valid name, try again " << endl
;
919 Name
= Database
.Find("CD::" + ID
);
921 string::iterator J
= Name
.begin();
922 for (; J
!= Name
.end(); J
++)
923 if (*J
== '/' || *J
== '"' || *J
== ':')
926 Database
.Set("CD::" + ID
,Name
);
927 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
929 // Copy the package files to the state directory
930 if (CopyPackages(CDROM
,Name
,List
) == false)
933 ReduceSourcelist(CDROM
,List
);
935 // Write the database and sourcelist
936 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
938 if (WriteDatabase(Database
) == false)
941 cout
<< "Writing new source list" << endl
;
942 if (WriteSourceList(Name
,List
) == false)
946 // Print the sourcelist entries
947 cout
<< "Source List entries for this Disc are:" << endl
;
948 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
950 string::size_type Space
= (*I
).find(' ');
951 if (Space
== string::npos
)
952 return _error
->Error("Internal error");
954 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
955 "\" " << string(*I
,Space
+1) << endl
;
962 // ShowHelp - Show the help screen /*{{{*/
963 // ---------------------------------------------------------------------
967 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
968 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
969 if (_config
->FindB("version") == true)
972 cout
<< "Usage: apt-cdrom [options] command" << endl
;
974 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
975 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
976 cout
<< "and /etc/fstab." << endl
;
978 cout
<< "Commands:" << endl
;
979 cout
<< " add - Add a CDROM" << endl
;
981 cout
<< "Options:" << endl
;
982 cout
<< " -h This help text" << endl
;
983 cout
<< " -d CD-ROM mount point" << endl
;
984 cout
<< " -r Rename a recognized CD-ROM" << endl
;
985 cout
<< " -m No mounting" << endl
;
986 cout
<< " -f Fast mode, don't check package files" << endl
;
987 cout
<< " -a Thorough scan mode" << endl
;
988 cout
<< " -c=? Read this configuration file" << endl
;
989 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
990 cout
<< "See fstab(5)" << endl
;
995 int main(int argc
,const char *argv
[])
997 CommandLine::Args Args
[] = {
998 {'h',"help","help",0},
999 {'v',"version","version",0},
1000 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
1001 {'r',"rename","APT::CDROM::Rename",0},
1002 {'m',"no-mount","APT::CDROM::NoMount",0},
1003 {'f',"fast","APT::CDROM::Fast",0},
1004 {'n',"just-print","APT::CDROM::NoAct",0},
1005 {'n',"recon","APT::CDROM::NoAct",0},
1006 {'n',"no-act","APT::CDROM::NoAct",0},
1007 {'a',"thorough","APT::CDROM::Thorough",0},
1008 {'c',"config-file",0,CommandLine::ConfigFile
},
1009 {'o',"option",0,CommandLine::ArbItem
},
1011 CommandLine::Dispatch Cmds
[] = {
1015 // Parse the command line and initialize the package library
1016 CommandLine
CmdL(Args
,_config
);
1017 if (pkgInitialize(*_config
) == false ||
1018 CmdL
.Parse(argc
,argv
) == false)
1020 _error
->DumpErrors();
1024 // See if the help should be shown
1025 if (_config
->FindB("help") == true ||
1026 CmdL
.FileSize() == 0)
1029 // Deal with stdout not being a tty
1030 if (ttyname(STDOUT_FILENO
) == 0 && _config
->FindI("quiet",0) < 1)
1031 _config
->Set("quiet","1");
1033 // Match the operation
1034 CmdL
.DispatchArg(Cmds
);
1036 // Print any errors or warnings found during parsing
1037 if (_error
->empty() == false)
1039 bool Errors
= _error
->PendingError();
1040 _error
->DumpErrors();
1041 return Errors
== true?100:0;