]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.5 1998/11/28 08:39:46 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/md5.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/progress.h>
20 #include <apt-pkg/tagfile.h>
29 #include <sys/errno.h>
38 // UnmountCdrom - Unmount a cdrom /*{{{*/
39 // ---------------------------------------------------------------------
41 bool UnmountCdrom(string Path
)
45 return _error
->Errno("fork","Failed to fork");
50 // Make all the fds /dev/null
51 for (int I
= 0; I
!= 10; I
++)
53 for (int I
= 0; I
!= 3; I
++)
54 dup2(open("/dev/null",O_RDWR
),I
);
58 Args
[1] = Path
.c_str();
60 execvp(Args
[0],(char **)Args
);
66 while (waitpid(Child
,&Status
,0) != Child
)
70 return _error
->Errno("waitpid","Couldn't wait for subprocess");
73 // Check for an error code.
74 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
79 // MountCdrom - Mount a cdrom /*{{{*/
80 // ---------------------------------------------------------------------
82 bool MountCdrom(string Path
)
86 return _error
->Errno("fork","Failed to fork");
91 // Make all the fds /dev/null
92 for (int I
= 0; I
!= 10; I
++)
94 for (int I
= 0; I
!= 3; I
++)
95 dup2(open("/dev/null",O_RDWR
),I
);
99 Args
[1] = Path
.c_str();
101 execvp(Args
[0],(char **)Args
);
107 while (waitpid(Child
,&Status
,0) != Child
)
111 return _error
->Errno("waitpid","Couldn't wait for subprocess");
114 // Check for an error code.
115 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
120 // IdentCdrom - Generate a unique string for this CD /*{{{*/
121 // ---------------------------------------------------------------------
122 /* We convert everything we hash into a string, this prevents byte size/order
123 from effecting the outcome. */
124 bool IdentCdrom(string CD
,string
&Res
)
128 string StartDir
= SafeGetCWD();
129 if (chdir(CD
.c_str()) != 0)
130 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
132 DIR *D
= opendir(".");
134 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
136 // Run over the directory
138 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
141 if (strcmp(Dir
->d_name
,".") == 0 ||
142 strcmp(Dir
->d_name
,"..") == 0)
145 sprintf(S
,"%lu",Dir
->d_ino
);
147 Hash
.Add(Dir
->d_name
);
150 chdir(StartDir
.c_str());
153 // Some stats from the fsys
155 if (statfs(CD
.c_str(),&Buf
) != 0)
156 return _error
->Errno("statfs","Failed to stat the cdrom");
158 sprintf(S
,"%u %u",Buf
.f_blocks
,Buf
.f_bfree
);
161 Res
= Hash
.Result().Value();
166 // FindPackages - Find the package files on the CDROM /*{{{*/
167 // ---------------------------------------------------------------------
168 /* We look over the cdrom for package files. This is a recursive
169 search that short circuits when it his a package file in the dir.
170 This speeds it up greatly as the majority of the size is in the
171 binary-* sub dirs. */
172 bool FindPackages(string CD
,vector
<string
> &List
, int Depth
= 0)
177 if (CD
[CD
.length()-1] != '/')
180 if (chdir(CD
.c_str()) != 0)
181 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
183 /* Aha! We found some package files. We assume that everything under
184 this dir is controlled by those package files so we don't look down
187 if (stat("Packages",&Buf
) == 0 ||
188 stat("Packages.gz",&Buf
) == 0)
194 DIR *D
= opendir(".");
196 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
198 // Run over the directory
199 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
202 if (strcmp(Dir
->d_name
,".") == 0 ||
203 strcmp(Dir
->d_name
,"..") == 0 ||
204 strcmp(Dir
->d_name
,"source") == 0 ||
205 strcmp(Dir
->d_name
,"experimental") == 0 ||
206 strcmp(Dir
->d_name
,"binary-all") == 0)
209 // See if the name is a sub directory
211 if (stat(Dir
->d_name
,&Buf
) != 0)
214 if (S_ISDIR(Buf
.st_mode
) == 0)
218 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
221 if (chdir(CD
.c_str()) != 0)
222 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
227 return !_error
->PendingError();
230 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
231 // ---------------------------------------------------------------------
232 /* Here we drop everything that is not this machines arch */
233 bool DropBinaryArch(vector
<string
> &List
)
236 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
238 for (unsigned int I
= 0; I
< List
.size(); I
++)
240 const char *Str
= List
[I
].c_str();
243 if ((Res
= strstr(Str
,"/binary-")) == 0)
247 if (strlen(Res
) < strlen(S
))
249 List
.erase(List
.begin() + I
);
254 // See if it is our arch
255 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
259 List
.erase(List
.begin() + I
);
266 // Score - We compute a 'score' for a path /*{{{*/
267 // ---------------------------------------------------------------------
268 /* Paths are scored based on how close they come to what I consider
269 normal. That is ones that have 'dist' 'stable' 'frozen' will score
270 higher than ones without. */
271 int Score(string Path
)
274 if (Path
.find("stable/") != string::npos
)
276 if (Path
.find("frozen/") != string::npos
)
278 if (Path
.find("/dists/") != string::npos
)
280 if (Path
.find("/main/") != string::npos
)
282 if (Path
.find("/contrib/") != string::npos
)
284 if (Path
.find("/non-free/") != string::npos
)
286 if (Path
.find("/non-US/") != string::npos
)
291 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
292 // ---------------------------------------------------------------------
293 /* Here we go and stat every file that we found and strip dup inodes. */
294 bool DropRepeats(vector
<string
> &List
)
296 // Get a list of all the inodes
297 ino_t
*Inodes
= new ino_t
[List
.size()];
298 for (unsigned int I
= 0; I
!= List
.size(); I
++)
301 if (stat(List
[I
].c_str(),&Buf
) != 0)
302 _error
->Errno("stat","Failed to stat %s",List
[I
].c_str());
303 Inodes
[I
] = Buf
.st_ino
;
307 for (unsigned int I
= 0; I
!= List
.size(); I
++)
309 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
312 if (Inodes
[J
] != Inodes
[I
])
315 // We score the two paths.. and erase one
316 int ScoreA
= Score(List
[I
]);
317 int ScoreB
= Score(List
[J
]);
328 // Wipe erased entries
329 for (unsigned int I
= 0; I
< List
.size();)
331 if (List
[I
].empty() == false)
334 List
.erase(List
.begin()+I
);
340 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
341 // ---------------------------------------------------------------------
342 /* We look for things in dists/ notation and convert them to
343 <dist> <component> form otherwise it is left alone. This also strips
345 void ConvertToSourceList(string CD
,string
&Path
)
348 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
350 // Strip the cdrom base path
351 Path
= string(Path
,CD
.length());
352 if (Path
.empty() == true)
355 // Too short to be a dists/ type
356 if (Path
.length() < strlen("dists/"))
360 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
364 string::size_type Slash
= strlen("dists/");
365 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
366 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
368 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
370 // Isolate the component
371 Slash
= Path
.find('/',Slash2
+1);
372 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
374 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
376 // Verify the trailing binar - bit
377 Slash2
= Path
.find('/',Slash
+ 1);
378 if (Slash
== string::npos
)
380 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
385 Path
= Dist
+ ' ' + Comp
;
388 // GrabFirst - Return the first Depth path components /*{{{*/
389 // ---------------------------------------------------------------------
391 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
393 string::size_type I
= 0;
396 I
= Path
.find('/',I
+1);
399 while (I
!= string::npos
&& Depth
!= 0);
401 if (I
== string::npos
)
404 To
= string(Path
,0,I
+1);
408 // ChopDirs - Chop off the leading directory components /*{{{*/
409 // ---------------------------------------------------------------------
411 string
ChopDirs(string Path
,unsigned int Depth
)
413 string::size_type I
= 0;
416 I
= Path
.find('/',I
+1);
419 while (I
!= string::npos
&& Depth
!= 0);
421 if (I
== string::npos
)
424 return string(Path
,I
+1);
427 // ReconstructPrefix - Fix strange prefixing /*{{{*/
428 // ---------------------------------------------------------------------
429 /* This prepends dir components from the path to the package files to
430 the path to the deb until it is found */
431 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
434 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
435 unsigned int Depth
= 1;
436 string MyPrefix
= Prefix
;
440 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
443 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
444 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
458 // ReconstructChop - Fixes bad source paths /*{{{*/
459 // ---------------------------------------------------------------------
460 /* This removes path components from the filename and prepends the location
461 of the package files until a file is found */
462 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
464 // Attempt to reconstruct the filename
465 unsigned long Depth
= 0;
469 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
471 File
= ChopDirs(File
,1);
473 if (File
.empty() == false)
487 // CopyPackages - Copy the package files from the CD /*{{{*/
488 // ---------------------------------------------------------------------
490 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
492 OpTextProgress Progress
;
494 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
495 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
497 // Prepare the progress indicator
498 unsigned long TotalSize
= 0;
499 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
502 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
503 return _error
->Errno("stat","Stat failed for %s",
504 string(*I
+ "Packages").c_str());
505 TotalSize
+= Buf
.st_size
;
508 unsigned long CurrentSize
= 0;
509 unsigned int NotFound
= 0;
510 unsigned int WrongSize
= 0;
511 unsigned int Packages
= 0;
512 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
514 string OrigPath
= string(*I
,CDROM
.length());
516 // Open the package file
517 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
518 pkgTagFile
Parser(Pkg
);
519 if (_error
->PendingError() == true)
522 // Open the output file
524 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
525 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
526 TargetF
+= URItoFileName(S
);
527 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
528 TargetF
= "/dev/null";
529 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
530 if (_error
->PendingError() == true)
533 // Setup the progress meter
534 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
535 "Reading Package Lists");
538 Progress
.SubProgress(Pkg
.Size());
539 pkgTagSection Section
;
541 unsigned long Hits
= 0;
542 unsigned long Chop
= 0;
543 while (Parser
.Step(Section
) == true)
545 Progress
.Progress(Parser
.Offset());
547 string File
= Section
.FindS("Filename");
548 unsigned long Size
= Section
.FindI("Size");
549 if (File
.empty() || Size
== 0)
550 return _error
->Error("Cannot find filename or size tag");
553 File
= OrigPath
+ ChopDirs(File
,Chop
);
555 // See if the file exists
556 if (NoStat
== false || Hits
< 10)
558 // Attempt to fix broken structure
561 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
562 ReconstructChop(Chop
,*I
,File
) == false)
568 File
= OrigPath
+ ChopDirs(File
,Chop
);
573 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
580 if ((unsigned)Buf
.st_size
!= Size
)
590 // Copy it to the target package file
595 // Mangle the output filename
596 const char *Filename
;
597 Section
.Find("Filename",Filename
,Stop
);
599 /* We need to rewrite the filename field so we emit
600 all fields except the filename file and rewrite that one */
601 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
603 Section
.Get(Start
,Stop
,I
);
604 if (Start
<= Filename
&& Stop
> Filename
)
607 sprintf(S
,"Filename: %s\n",File
.c_str());
608 if (I
+ 1 == Section
.Count())
610 if (Target
.Write(S
,strlen(S
)) == false)
614 if (Target
.Write(Start
,Stop
-Start
) == false)
617 if (Target
.Write("\n",1) == false)
622 Section
.GetSection(Start
,Stop
);
623 if (Target
.Write(Start
,Stop
-Start
) == false)
629 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
631 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
633 // Move out of the partial directory
635 string FinalF
= _config
->FindDir("Dir::State::lists");
636 FinalF
+= URItoFileName(S
);
637 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
638 return _error
->Errno("rename","Failed to rename");
640 // Copy the release file
641 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
642 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
643 TargetF
+= URItoFileName(S
);
644 if (FileExists(*I
+ "Release") == true)
646 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
647 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
648 if (_error
->PendingError() == true)
651 if (CopyFile(Rel
,Target
) == false)
656 // Empty release file
657 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
660 // Rename the release file
661 FinalF
= _config
->FindDir("Dir::State::lists");
662 FinalF
+= URItoFileName(S
);
663 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
664 return _error
->Errno("rename","Failed to rename");
667 /* Mangle the source to be in the proper notation with
668 prefix dist [component] */
669 *I
= string(*I
,Prefix
.length());
670 ConvertToSourceList(CDROM
,*I
);
671 *I
= Prefix
+ ' ' + *I
;
673 CurrentSize
+= Pkg
.Size();
678 cout
<< "Wrote " << Packages
<< " package records" ;
680 cout
<< " with " << NotFound
<< " missing files";
681 if (NotFound
!= 0 && WrongSize
!= 0)
684 cout
<< " with " << WrongSize
<< " mismatched files";
688 return _error
->Error("No valid package records were found.");
690 if (NotFound
+ WrongSize
> 10)
691 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
695 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
696 // ---------------------------------------------------------------------
697 /* This takes the list of source list expressed entires and collects
698 similar ones to form a single entry for each dist */
699 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
701 sort(List
.begin(),List
.end());
703 // Collect similar entries
704 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
707 string::size_type Space
= (*I
).find(' ');
708 if (Space
== string::npos
)
710 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
711 if (SSpace
== string::npos
)
714 string Word1
= string(*I
,Space
,SSpace
-Space
);
715 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
718 string::size_type Space2
= (*J
).find(' ');
719 if (Space2
== string::npos
)
721 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
722 if (SSpace2
== string::npos
)
725 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
728 *J
+= string(*I
,SSpace
);
733 // Wipe erased entries
734 for (unsigned int I
= 0; I
< List
.size();)
736 if (List
[I
].empty() == false)
739 List
.erase(List
.begin()+I
);
743 // WriteDatabase - Write the CDROM Database file /*{{{*/
744 // ---------------------------------------------------------------------
745 /* We rewrite the configuration class associated with the cdrom database. */
746 bool WriteDatabase(Configuration
&Cnf
)
748 string DFile
= _config
->FindFile("Dir::State::cdroms");
749 string NewFile
= DFile
+ ".new";
751 unlink(NewFile
.c_str());
752 ofstream
Out(NewFile
.c_str());
754 return _error
->Errno("ofstream::ofstream",
755 "Failed to open %s.new",DFile
.c_str());
757 /* Write out all of the configuration directives by walking the
758 configuration tree */
759 const Configuration::Item
*Top
= Cnf
.Tree(0);
762 // Print the config entry
763 if (Top
->Value
.empty() == false)
764 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
772 while (Top
!= 0 && Top
->Next
== 0)
780 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
781 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
782 return _error
->Errno("rename","Failed to rename %s.new to %s",
783 DFile
.c_str(),DFile
.c_str());
788 // WriteSourceList - Write an updated sourcelist /*{{{*/
789 // ---------------------------------------------------------------------
790 /* This reads the old source list and copies it into the new one. It
791 appends the new CDROM entires just after the first block of comments.
792 This places them first in the file. It also removes any old entries
793 that were the same. */
794 bool WriteSourceList(string Name
,vector
<string
> &List
)
796 string File
= _config
->FindFile("Dir::Etc::sourcelist");
798 // Open the stream for reading
799 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
801 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
803 string NewFile
= File
+ ".new";
804 unlink(NewFile
.c_str());
805 ofstream
Out(NewFile
.c_str());
807 return _error
->Errno("ofstream::ofstream",
808 "Failed to open %s.new",File
.c_str());
810 // Create a short uri without the path
811 string ShortURI
= "cdrom:" + Name
+ "/";
816 while (F
.eof() == false)
818 F
.getline(Buffer
,sizeof(Buffer
));
820 _strtabexpand(Buffer
,sizeof(Buffer
));
824 if (Buffer
[0] == '#' || Buffer
[0] == 0)
826 Out
<< Buffer
<< endl
;
832 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
834 string::size_type Space
= (*I
).find(' ');
835 if (Space
== string::npos
)
836 return _error
->Error("Internal error");
838 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
839 "\" " << string(*I
,Space
+1) << endl
;
848 if (ParseQuoteWord(C
,Type
) == false ||
849 ParseQuoteWord(C
,URI
) == false)
851 Out
<< Buffer
<< endl
;
855 // Emit lines like this one
856 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
858 Out
<< Buffer
<< endl
;
863 // Just in case the file was empty
866 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
868 string::size_type Space
= (*I
).find(' ');
869 if (Space
== string::npos
)
870 return _error
->Error("Internal error");
872 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
873 "\" " << string(*I
,Space
+1) << endl
;
879 rename(File
.c_str(),string(File
+ '~').c_str());
880 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
881 return _error
->Errno("rename","Failed to rename %s.new to %s",
882 File
.c_str(),File
.c_str());
888 // Prompt - Simple prompt /*{{{*/
889 // ---------------------------------------------------------------------
891 void Prompt(const char *Text
)
894 cout
<< Text
<< ' ' << flush
;
895 read(STDIN_FILENO
,&C
,1);
900 // PromptLine - Prompt for an input line /*{{{*/
901 // ---------------------------------------------------------------------
903 string
PromptLine(const char *Text
)
905 cout
<< Text
<< ':' << endl
;
913 // DoAdd - Add a new CDROM /*{{{*/
914 // ---------------------------------------------------------------------
915 /* This does the main add bit.. We show some status and things. The
916 sequence is to mount/umount the CD, Ident it then scan it for package
917 files and reduce that list. Then we copy over the package files and
918 verify them. Then rewrite the database files */
919 bool DoAdd(CommandLine
&)
922 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
923 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
926 Configuration Database
;
927 string DFile
= _config
->FindFile("Dir::State::cdroms");
928 if (FileExists(DFile
) == true)
930 if (ReadConfigFile(Database
,DFile
) == false)
931 return _error
->Error("Unable to read the cdrom database %s",
935 // Unmount the CD and get the user to put in the one they want
936 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
938 cout
<< "Unmounting CD-ROM" << endl
;
941 // Mount the new CDROM
942 Prompt("Please insert a Disc in the drive and press any key");
943 cout
<< "Mounting CD-ROM" << endl
;
944 if (MountCdrom(CDROM
) == false)
946 cout
<< "Failed to mount the cdrom." << endl
;
951 // Hash the CD to get an ID
952 cout
<< "Identifying.. " << flush
;
954 if (IdentCdrom(CDROM
,ID
) == false)
956 cout
<< '[' << ID
<< ']' << endl
;
958 cout
<< "Scanning Disc for index files.. " << flush
;
959 // Get the CD structure
961 string StartDir
= SafeGetCWD();
962 if (FindPackages(CDROM
,List
) == false)
964 chdir(StartDir
.c_str());
966 if (_config
->FindB("Debug::aptcdrom",false) == true)
968 cout
<< "I found:" << endl
;
969 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
976 DropBinaryArch(List
);
978 cout
<< "Found " << List
.size() << " package index files." << endl
;
980 if (List
.size() == 0)
981 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
983 // Check if the CD is in the database
985 if (Database
.Exists("CD::" + ID
) == false ||
986 _config
->FindB("APT::CDROM::Rename",false) == true)
988 // Try to use the CDs label if at all possible
989 if (FileExists(CDROM
+ "/.disk/info") == true)
991 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
995 if (Name
.empty() == false)
997 cout
<< "Found label '" << Name
<< "'" << endl
;
998 Database
.Set("CD::" + ID
+ "::Label",Name
);
1002 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
1003 Name
.empty() == true)
1005 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
1008 Name
= PromptLine("");
1009 if (Name
.empty() == false &&
1010 Name
.find('/') == string::npos
)
1012 cout
<< "That is not a valid name, try again " << endl
;
1018 Name
= Database
.Find("CD::" + ID
);
1019 Database
.Set("CD::" + ID
,Name
);
1020 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
1022 // Copy the package files to the state directory
1023 if (CopyPackages(CDROM
,Name
,List
) == false)
1026 ReduceSourcelist(CDROM
,List
);
1028 // Write the database and sourcelist
1029 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
1031 if (WriteDatabase(Database
) == false)
1034 cout
<< "Writing new source list" << endl
;
1035 if (WriteSourceList(Name
,List
) == false)
1039 // Print the sourcelist entries
1040 cout
<< "Source List entires for this Disc are:" << endl
;
1041 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
1043 string::size_type Space
= (*I
).find(' ');
1044 if (Space
== string::npos
)
1045 return _error
->Error("Internal error");
1047 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
1048 "\" " << string(*I
,Space
+1) << endl
;
1055 // ShowHelp - Show the help screen /*{{{*/
1056 // ---------------------------------------------------------------------
1060 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
1061 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
1063 cout
<< "Usage: apt-cdrom [options] command" << endl
;
1065 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
1066 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
1067 cout
<< "and /etc/fstab." << endl
;
1069 cout
<< "Commands:" << endl
;
1070 cout
<< " add - Add a CDROM" << endl
;
1072 cout
<< "Options:" << endl
;
1073 cout
<< " -h This help text" << endl
;
1074 cout
<< " -d CD-ROM mount point" << endl
;
1075 cout
<< " -r Rename a recognized CD-ROM" << endl
;
1076 cout
<< " -m No mounting" << endl
;
1077 cout
<< " -f Fast mode, don't check package files" << endl
;
1078 cout
<< " -c=? Read this configuration file" << endl
;
1079 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
1080 cout
<< "See fstab(5)" << endl
;
1085 int main(int argc
,const char *argv
[])
1087 CommandLine::Args Args
[] = {
1088 {'h',"help","help",0},
1089 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
1090 {'r',"rename","APT::CDROM::Rename",0},
1091 {'m',"no-mount","APT::CDROM::NoMount",0},
1092 {'f',"fast","APT::CDROM::Fast",0},
1093 {'n',"just-print","APT::CDROM::NoAct",0},
1094 {'n',"recon","APT::CDROM::NoAct",0},
1095 {'n',"no-act","APT::CDROM::NoAct",0},
1096 {'c',"config-file",0,CommandLine::ConfigFile
},
1097 {'o',"option",0,CommandLine::ArbItem
},
1099 CommandLine::Dispatch Cmds
[] = {
1103 // Parse the command line and initialize the package library
1104 CommandLine
CmdL(Args
,_config
);
1105 if (pkgInitialize(*_config
) == false ||
1106 CmdL
.Parse(argc
,argv
) == false)
1108 _error
->DumpErrors();
1112 // See if the help should be shown
1113 if (_config
->FindB("help") == true ||
1114 CmdL
.FileSize() == 0)
1117 // Match the operation
1118 CmdL
.DispatchArg(Cmds
);
1120 // Print any errors or warnings found during parsing
1121 if (_error
->empty() == false)
1123 bool Errors
= _error
->PendingError();
1124 _error
->DumpErrors();
1125 return Errors
== true?100:0;