]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.23 1999/05/29 03:32:30 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
,string
&InfoDir
,
42 unsigned int Depth
= 0)
44 static ino_t Inodes
[9];
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 // Look for a .disk subdirectory
56 if (stat(".disk",&Buf
) == 0)
58 if (InfoDir
.empty() == true)
59 InfoDir
= CD
+ ".disk/";
62 /* Aha! We found some package files. We assume that everything under
63 this dir is controlled by those package files so we don't look down
65 if (stat("Packages",&Buf
) == 0)
69 // Continue down if thorough is given
70 if (_config
->FindB("APT::CDROM::Thorough",false) == false)
74 DIR *D
= opendir(".");
76 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
78 // Run over the directory
79 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
82 if (strcmp(Dir
->d_name
,".") == 0 ||
83 strcmp(Dir
->d_name
,"..") == 0 ||
84 strcmp(Dir
->d_name
,"source") == 0 ||
85 strcmp(Dir
->d_name
,"experimental") == 0 ||
86 strcmp(Dir
->d_name
,"binary-all") == 0)
89 // See if the name is a sub directory
91 if (stat(Dir
->d_name
,&Buf
) != 0)
94 if (S_ISDIR(Buf
.st_mode
) == 0)
98 for (I
= 0; I
!= Depth
; I
++)
99 if (Inodes
[I
] == Buf
.st_ino
)
104 // Store the inodes weve seen
105 Inodes
[Depth
] = Buf
.st_ino
;
108 if (FindPackages(CD
+ Dir
->d_name
,List
,InfoDir
,Depth
+1) == false)
111 if (chdir(CD
.c_str()) != 0)
112 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
117 return !_error
->PendingError();
120 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
121 // ---------------------------------------------------------------------
122 /* Here we drop everything that is not this machines arch */
123 bool DropBinaryArch(vector
<string
> &List
)
126 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
128 for (unsigned int I
= 0; I
< List
.size(); I
++)
130 const char *Str
= List
[I
].c_str();
133 if ((Res
= strstr(Str
,"/binary-")) == 0)
137 if (strlen(Res
) < strlen(S
))
139 List
.erase(List
.begin() + I
);
144 // See if it is our arch
145 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
149 List
.erase(List
.begin() + I
);
156 // Score - We compute a 'score' for a path /*{{{*/
157 // ---------------------------------------------------------------------
158 /* Paths are scored based on how close they come to what I consider
159 normal. That is ones that have 'dist' 'stable' 'frozen' will score
160 higher than ones without. */
161 int Score(string Path
)
164 if (Path
.find("stable/") != string::npos
)
166 if (Path
.find("/binary-") != string::npos
)
168 if (Path
.find("frozen/") != string::npos
)
170 if (Path
.find("/dists/") != string::npos
)
172 if (Path
.find("/main/") != string::npos
)
174 if (Path
.find("/contrib/") != string::npos
)
176 if (Path
.find("/non-free/") != string::npos
)
178 if (Path
.find("/non-US/") != string::npos
)
183 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
184 // ---------------------------------------------------------------------
185 /* Here we go and stat every file that we found and strip dup inodes. */
186 bool DropRepeats(vector
<string
> &List
)
188 // Get a list of all the inodes
189 ino_t
*Inodes
= new ino_t
[List
.size()];
190 for (unsigned int I
= 0; I
!= List
.size(); I
++)
193 if (stat((List
[I
] + "Packages").c_str(),&Buf
) != 0)
194 _error
->Errno("stat","Failed to stat %sPackages",List
[I
].c_str());
195 Inodes
[I
] = Buf
.st_ino
;
198 if (_error
->PendingError() == true)
202 for (unsigned int I
= 0; I
!= List
.size(); I
++)
204 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
207 if (Inodes
[J
] != Inodes
[I
])
210 // We score the two paths.. and erase one
211 int ScoreA
= Score(List
[I
]);
212 int ScoreB
= Score(List
[J
]);
223 // Wipe erased entries
224 for (unsigned int I
= 0; I
< List
.size();)
226 if (List
[I
].empty() == false)
229 List
.erase(List
.begin()+I
);
235 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
236 // ---------------------------------------------------------------------
237 /* We look for things in dists/ notation and convert them to
238 <dist> <component> form otherwise it is left alone. This also strips
240 void ConvertToSourceList(string CD
,string
&Path
)
243 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
245 // Strip the cdrom base path
246 Path
= string(Path
,CD
.length());
247 if (Path
.empty() == true)
250 // Too short to be a dists/ type
251 if (Path
.length() < strlen("dists/"))
255 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
259 string::size_type Slash
= strlen("dists/");
260 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
261 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
263 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
265 // Isolate the component
266 Slash
= Path
.find('/',Slash2
+1);
267 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
269 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
271 // Verify the trailing binar - bit
272 Slash2
= Path
.find('/',Slash
+ 1);
273 if (Slash
== string::npos
)
275 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
280 Path
= Dist
+ ' ' + Comp
;
283 // GrabFirst - Return the first Depth path components /*{{{*/
284 // ---------------------------------------------------------------------
286 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
288 string::size_type I
= 0;
291 I
= Path
.find('/',I
+1);
294 while (I
!= string::npos
&& Depth
!= 0);
296 if (I
== string::npos
)
299 To
= string(Path
,0,I
+1);
303 // ChopDirs - Chop off the leading directory components /*{{{*/
304 // ---------------------------------------------------------------------
306 string
ChopDirs(string Path
,unsigned int Depth
)
308 string::size_type I
= 0;
311 I
= Path
.find('/',I
+1);
314 while (I
!= string::npos
&& Depth
!= 0);
316 if (I
== string::npos
)
319 return string(Path
,I
+1);
322 // ReconstructPrefix - Fix strange prefixing /*{{{*/
323 // ---------------------------------------------------------------------
324 /* This prepends dir components from the path to the package files to
325 the path to the deb until it is found */
326 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
329 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
330 unsigned int Depth
= 1;
331 string MyPrefix
= Prefix
;
335 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
338 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
339 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
353 // ReconstructChop - Fixes bad source paths /*{{{*/
354 // ---------------------------------------------------------------------
355 /* This removes path components from the filename and prepends the location
356 of the package files until a file is found */
357 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
359 // Attempt to reconstruct the filename
360 unsigned long Depth
= 0;
364 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
366 File
= ChopDirs(File
,1);
368 if (File
.empty() == false)
382 // CopyPackages - Copy the package files from the CD /*{{{*/
383 // ---------------------------------------------------------------------
385 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
387 OpTextProgress Progress
;
389 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
390 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
392 // Prepare the progress indicator
393 unsigned long TotalSize
= 0;
394 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
397 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
398 return _error
->Errno("stat","Stat failed for %s",
399 string(*I
+ "Packages").c_str());
400 TotalSize
+= Buf
.st_size
;
403 unsigned long CurrentSize
= 0;
404 unsigned int NotFound
= 0;
405 unsigned int WrongSize
= 0;
406 unsigned int Packages
= 0;
407 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
409 string OrigPath
= string(*I
,CDROM
.length());
411 // Open the package file
412 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
413 pkgTagFile
Parser(Pkg
);
414 if (_error
->PendingError() == true)
417 // Open the output file
419 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
420 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
421 TargetF
+= URItoFileName(S
);
422 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
423 TargetF
= "/dev/null";
424 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
425 if (_error
->PendingError() == true)
428 // Setup the progress meter
429 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
430 "Reading Package Lists");
433 Progress
.SubProgress(Pkg
.Size());
434 pkgTagSection Section
;
436 unsigned long Hits
= 0;
437 unsigned long Chop
= 0;
438 while (Parser
.Step(Section
) == true)
440 Progress
.Progress(Parser
.Offset());
442 string File
= Section
.FindS("Filename");
443 unsigned long Size
= Section
.FindI("Size");
444 if (File
.empty() || Size
== 0)
445 return _error
->Error("Cannot find filename or size tag");
448 File
= OrigPath
+ ChopDirs(File
,Chop
);
450 // See if the file exists
451 if (NoStat
== false || Hits
< 10)
453 // Attempt to fix broken structure
456 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
457 ReconstructChop(Chop
,*I
,File
) == false)
463 File
= OrigPath
+ ChopDirs(File
,Chop
);
468 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
475 if ((unsigned)Buf
.st_size
!= Size
)
485 // Copy it to the target package file
490 // Mangle the output filename
491 const char *Filename
;
492 Section
.Find("Filename",Filename
,Stop
);
494 /* We need to rewrite the filename field so we emit
495 all fields except the filename file and rewrite that one */
496 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
498 Section
.Get(Start
,Stop
,I
);
499 if (Start
<= Filename
&& Stop
> Filename
)
502 sprintf(S
,"Filename: %s\n",File
.c_str());
503 if (I
+ 1 == Section
.Count())
505 if (Target
.Write(S
,strlen(S
)) == false)
510 if (Target
.Write(Start
,Stop
-Start
) == false)
512 if (Stop
[-1] != '\n')
513 if (Target
.Write("\n",1) == false)
517 if (Target
.Write("\n",1) == false)
522 Section
.GetSection(Start
,Stop
);
523 if (Target
.Write(Start
,Stop
-Start
) == false)
529 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
531 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
533 // Move out of the partial directory
535 string FinalF
= _config
->FindDir("Dir::State::lists");
536 FinalF
+= URItoFileName(S
);
537 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
538 return _error
->Errno("rename","Failed to rename");
540 // Copy the release file
541 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
542 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
543 TargetF
+= URItoFileName(S
);
544 if (FileExists(*I
+ "Release") == true)
546 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
547 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
548 if (_error
->PendingError() == true)
551 if (CopyFile(Rel
,Target
) == false)
556 // Empty release file
557 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
560 // Rename the release file
561 FinalF
= _config
->FindDir("Dir::State::lists");
562 FinalF
+= URItoFileName(S
);
563 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
564 return _error
->Errno("rename","Failed to rename");
567 /* Mangle the source to be in the proper notation with
568 prefix dist [component] */
569 *I
= string(*I
,Prefix
.length());
570 ConvertToSourceList(CDROM
,*I
);
571 *I
= Prefix
+ ' ' + *I
;
573 CurrentSize
+= Pkg
.Size();
578 cout
<< "Wrote " << Packages
<< " package records" ;
580 cout
<< " with " << NotFound
<< " missing files";
581 if (NotFound
!= 0 && WrongSize
!= 0)
584 cout
<< " with " << WrongSize
<< " mismatched files";
588 return _error
->Error("No valid package records were found.");
590 if (NotFound
+ WrongSize
> 10)
591 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
597 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
598 // ---------------------------------------------------------------------
599 /* This takes the list of source list expressed entires and collects
600 similar ones to form a single entry for each dist */
601 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
603 sort(List
.begin(),List
.end());
605 // Collect similar entries
606 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
609 string::size_type Space
= (*I
).find(' ');
610 if (Space
== string::npos
)
612 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
613 if (SSpace
== string::npos
)
616 string Word1
= string(*I
,Space
,SSpace
-Space
);
617 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
620 string::size_type Space2
= (*J
).find(' ');
621 if (Space2
== string::npos
)
623 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
624 if (SSpace2
== string::npos
)
627 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
630 *J
+= string(*I
,SSpace
);
635 // Wipe erased entries
636 for (unsigned int I
= 0; I
< List
.size();)
638 if (List
[I
].empty() == false)
641 List
.erase(List
.begin()+I
);
645 // WriteDatabase - Write the CDROM Database file /*{{{*/
646 // ---------------------------------------------------------------------
647 /* We rewrite the configuration class associated with the cdrom database. */
648 bool WriteDatabase(Configuration
&Cnf
)
650 string DFile
= _config
->FindFile("Dir::State::cdroms");
651 string NewFile
= DFile
+ ".new";
653 unlink(NewFile
.c_str());
654 ofstream
Out(NewFile
.c_str());
656 return _error
->Errno("ofstream::ofstream",
657 "Failed to open %s.new",DFile
.c_str());
659 /* Write out all of the configuration directives by walking the
660 configuration tree */
661 const Configuration::Item
*Top
= Cnf
.Tree(0);
664 // Print the config entry
665 if (Top
->Value
.empty() == false)
666 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
674 while (Top
!= 0 && Top
->Next
== 0)
682 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
683 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
684 return _error
->Errno("rename","Failed to rename %s.new to %s",
685 DFile
.c_str(),DFile
.c_str());
690 // WriteSourceList - Write an updated sourcelist /*{{{*/
691 // ---------------------------------------------------------------------
692 /* This reads the old source list and copies it into the new one. It
693 appends the new CDROM entires just after the first block of comments.
694 This places them first in the file. It also removes any old entries
695 that were the same. */
696 bool WriteSourceList(string Name
,vector
<string
> &List
)
698 string File
= _config
->FindFile("Dir::Etc::sourcelist");
700 // Open the stream for reading
701 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
703 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
705 string NewFile
= File
+ ".new";
706 unlink(NewFile
.c_str());
707 ofstream
Out(NewFile
.c_str());
709 return _error
->Errno("ofstream::ofstream",
710 "Failed to open %s.new",File
.c_str());
712 // Create a short uri without the path
713 string ShortURI
= "cdrom:" + Name
+ "/";
718 while (F
.eof() == false)
720 F
.getline(Buffer
,sizeof(Buffer
));
722 _strtabexpand(Buffer
,sizeof(Buffer
));
726 if (Buffer
[0] == '#' || Buffer
[0] == 0)
728 Out
<< Buffer
<< endl
;
734 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
736 string::size_type Space
= (*I
).find(' ');
737 if (Space
== string::npos
)
738 return _error
->Error("Internal error");
740 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
741 "\" " << string(*I
,Space
+1) << endl
;
750 if (ParseQuoteWord(C
,Type
) == false ||
751 ParseQuoteWord(C
,URI
) == false)
753 Out
<< Buffer
<< endl
;
757 // Emit lines like this one
758 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
760 Out
<< Buffer
<< endl
;
765 // Just in case the file was empty
768 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
770 string::size_type Space
= (*I
).find(' ');
771 if (Space
== string::npos
)
772 return _error
->Error("Internal error");
774 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
775 "\" " << string(*I
,Space
+1) << endl
;
781 rename(File
.c_str(),string(File
+ '~').c_str());
782 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
783 return _error
->Errno("rename","Failed to rename %s.new to %s",
784 File
.c_str(),File
.c_str());
790 // Prompt - Simple prompt /*{{{*/
791 // ---------------------------------------------------------------------
793 void Prompt(const char *Text
)
796 cout
<< Text
<< ' ' << flush
;
797 read(STDIN_FILENO
,&C
,1);
802 // PromptLine - Prompt for an input line /*{{{*/
803 // ---------------------------------------------------------------------
805 string
PromptLine(const char *Text
)
807 cout
<< Text
<< ':' << endl
;
815 // DoAdd - Add a new CDROM /*{{{*/
816 // ---------------------------------------------------------------------
817 /* This does the main add bit.. We show some status and things. The
818 sequence is to mount/umount the CD, Ident it then scan it for package
819 files and reduce that list. Then we copy over the package files and
820 verify them. Then rewrite the database files */
821 bool DoAdd(CommandLine
&)
824 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
826 CDROM
= SafeGetCWD() + '/' + CDROM
;
828 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
831 Configuration Database
;
832 string DFile
= _config
->FindFile("Dir::State::cdroms");
833 if (FileExists(DFile
) == true)
835 if (ReadConfigFile(Database
,DFile
) == false)
836 return _error
->Error("Unable to read the cdrom database %s",
840 // Unmount the CD and get the user to put in the one they want
841 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
843 cout
<< "Unmounting CD-ROM" << endl
;
846 // Mount the new CDROM
847 Prompt("Please insert a Disc in the drive and press enter");
848 cout
<< "Mounting CD-ROM" << endl
;
849 if (MountCdrom(CDROM
) == false)
850 return _error
->Error("Failed to mount the cdrom.");
853 // Hash the CD to get an ID
854 cout
<< "Identifying.. " << flush
;
856 if (IdentCdrom(CDROM
,ID
) == false)
862 cout
<< '[' << ID
<< ']' << endl
;
864 cout
<< "Scanning Disc for index files.. " << flush
;
865 // Get the CD structure
867 string StartDir
= SafeGetCWD();
869 if (FindPackages(CDROM
,List
,InfoDir
) == false)
875 chdir(StartDir
.c_str());
877 if (_config
->FindB("Debug::aptcdrom",false) == true)
879 cout
<< "I found:" << endl
;
880 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
887 DropBinaryArch(List
);
889 cout
<< "Found " << List
.size() << " package index files." << endl
;
891 if (List
.size() == 0)
892 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
894 // Check if the CD is in the database
896 if (Database
.Exists("CD::" + ID
) == false ||
897 _config
->FindB("APT::CDROM::Rename",false) == true)
899 // Try to use the CDs label if at all possible
900 if (InfoDir
.empty() == false &&
901 FileExists(InfoDir
+ "/info") == true)
903 ifstream
F(string(InfoDir
+ "/info").c_str());
907 if (Name
.empty() == false)
909 cout
<< "Found label '" << Name
<< "'" << endl
;
910 Database
.Set("CD::" + ID
+ "::Label",Name
);
914 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
915 Name
.empty() == true)
917 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
920 Name
= PromptLine("");
921 if (Name
.empty() == false &&
922 Name
.find('"') == string::npos
&&
923 Name
.find(':') == string::npos
&&
924 Name
.find('/') == string::npos
)
926 cout
<< "That is not a valid name, try again " << endl
;
931 Name
= Database
.Find("CD::" + ID
);
933 string::iterator J
= Name
.begin();
934 for (; J
!= Name
.end(); J
++)
935 if (*J
== '/' || *J
== '"' || *J
== ':')
938 Database
.Set("CD::" + ID
,Name
);
939 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
941 // Copy the package files to the state directory
942 if (CopyPackages(CDROM
,Name
,List
) == false)
945 ReduceSourcelist(CDROM
,List
);
947 // Write the database and sourcelist
948 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
950 if (WriteDatabase(Database
) == false)
953 cout
<< "Writing new source list" << endl
;
954 if (WriteSourceList(Name
,List
) == false)
958 // Print the sourcelist entries
959 cout
<< "Source List entries for this Disc are:" << endl
;
960 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
962 string::size_type Space
= (*I
).find(' ');
963 if (Space
== string::npos
)
964 return _error
->Error("Internal error");
966 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
967 "\" " << string(*I
,Space
+1) << endl
;
974 // ShowHelp - Show the help screen /*{{{*/
975 // ---------------------------------------------------------------------
979 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
980 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
981 if (_config
->FindB("version") == true)
984 cout
<< "Usage: apt-cdrom [options] command" << endl
;
986 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
987 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
988 cout
<< "and /etc/fstab." << endl
;
990 cout
<< "Commands:" << endl
;
991 cout
<< " add - Add a CDROM" << endl
;
993 cout
<< "Options:" << endl
;
994 cout
<< " -h This help text" << endl
;
995 cout
<< " -d CD-ROM mount point" << endl
;
996 cout
<< " -r Rename a recognized CD-ROM" << endl
;
997 cout
<< " -m No mounting" << endl
;
998 cout
<< " -f Fast mode, don't check package files" << endl
;
999 cout
<< " -a Thorough scan mode" << endl
;
1000 cout
<< " -c=? Read this configuration file" << endl
;
1001 cout
<< " -o=? Set an arbitary configuration option, eg -o dir::cache=/tmp" << endl
;
1002 cout
<< "See fstab(5)" << endl
;
1007 int main(int argc
,const char *argv
[])
1009 CommandLine::Args Args
[] = {
1010 {'h',"help","help",0},
1011 {'v',"version","version",0},
1012 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
1013 {'r',"rename","APT::CDROM::Rename",0},
1014 {'m',"no-mount","APT::CDROM::NoMount",0},
1015 {'f',"fast","APT::CDROM::Fast",0},
1016 {'n',"just-print","APT::CDROM::NoAct",0},
1017 {'n',"recon","APT::CDROM::NoAct",0},
1018 {'n',"no-act","APT::CDROM::NoAct",0},
1019 {'a',"thorough","APT::CDROM::Thorough",0},
1020 {'c',"config-file",0,CommandLine::ConfigFile
},
1021 {'o',"option",0,CommandLine::ArbItem
},
1023 CommandLine::Dispatch Cmds
[] = {
1027 // Parse the command line and initialize the package library
1028 CommandLine
CmdL(Args
,_config
);
1029 if (pkgInitialize(*_config
) == false ||
1030 CmdL
.Parse(argc
,argv
) == false)
1032 _error
->DumpErrors();
1036 // See if the help should be shown
1037 if (_config
->FindB("help") == true ||
1038 CmdL
.FileSize() == 0)
1041 // Deal with stdout not being a tty
1042 if (ttyname(STDOUT_FILENO
) == 0 && _config
->FindI("quiet",0) < 1)
1043 _config
->Set("quiet","1");
1045 // Match the operation
1046 CmdL
.DispatchArg(Cmds
);
1048 // Print any errors or warnings found during parsing
1049 if (_error
->empty() == false)
1051 bool Errors
= _error
->PendingError();
1052 _error
->DumpErrors();
1053 return Errors
== true?100:0;