]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
d524cc0e6da8b133735d9b20d40a019e6a597075
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.4 1998/11/28 03:54:34 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());
353 // Too short to be a dists/ type
354 if (Path
.length() < strlen("dists/"))
358 if (stringcmp(Path
.begin(),Path
.begin()+strlen("dists/"),"dists/") != 0)
362 string::size_type Slash
= strlen("dists/");
363 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
364 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
366 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
368 // Isolate the component
369 Slash
= Path
.find('/',Slash2
+1);
370 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
372 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
374 // Verify the trailing binar - bit
375 Slash2
= Path
.find('/',Slash
+ 1);
376 if (Slash
== string::npos
)
378 string Binary
= string(Path
,Slash
+1,Slash2
- Slash
-1);
383 Path
= Dist
+ ' ' + Comp
;
386 // GrabFirst - Return the first Depth path components /*{{{*/
387 // ---------------------------------------------------------------------
389 bool GrabFirst(string Path
,string
&To
,unsigned int Depth
)
391 string::size_type I
= 0;
394 I
= Path
.find('/',I
+1);
397 while (I
!= string::npos
&& Depth
!= 0);
399 if (I
== string::npos
)
402 To
= string(Path
,0,I
+1);
406 // ChopDirs - Chop off the leading directory components /*{{{*/
407 // ---------------------------------------------------------------------
409 string
ChopDirs(string Path
,unsigned int Depth
)
411 string::size_type I
= 0;
414 I
= Path
.find('/',I
+1);
417 while (I
!= string::npos
&& Depth
!= 0);
419 if (I
== string::npos
)
422 return string(Path
,I
+1);
425 // ReconstructPrefix - Fix strange prefixing /*{{{*/
426 // ---------------------------------------------------------------------
427 /* This prepends dir components from the path to the package files to
428 the path to the deb until it is found */
429 bool ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
432 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
433 unsigned int Depth
= 1;
434 string MyPrefix
= Prefix
;
438 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
441 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
442 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
456 // ReconstructChop - Fixes bad source paths /*{{{*/
457 // ---------------------------------------------------------------------
458 /* This removes path components from the filename and prepends the location
459 of the package files until a file is found */
460 bool ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
462 // Attempt to reconstruct the filename
463 unsigned long Depth
= 0;
467 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
469 File
= ChopDirs(File
,1);
471 if (File
.empty() == false)
485 // CopyPackages - Copy the package files from the CD /*{{{*/
486 // ---------------------------------------------------------------------
488 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
490 OpTextProgress Progress
;
492 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
493 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
495 // Prepare the progress indicator
496 unsigned long TotalSize
= 0;
497 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
500 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
501 return _error
->Errno("stat","Stat failed for %s",
502 string(*I
+ "Packages").c_str());
503 TotalSize
+= Buf
.st_size
;
506 unsigned long CurrentSize
= 0;
507 unsigned int NotFound
= 0;
508 unsigned int WrongSize
= 0;
509 unsigned int Packages
= 0;
510 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
512 string OrigPath
= string(*I
,CDROM
.length());
514 // Open the package file
515 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
516 pkgTagFile
Parser(Pkg
);
517 if (_error
->PendingError() == true)
520 // Open the output file
522 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
523 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
524 TargetF
+= URItoFileName(S
);
525 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
526 TargetF
= "/dev/null";
527 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
528 if (_error
->PendingError() == true)
531 // Setup the progress meter
532 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
533 "Reading Package Lists");
536 Progress
.SubProgress(Pkg
.Size());
537 pkgTagSection Section
;
539 unsigned long Hits
= 0;
540 unsigned long Chop
= 0;
541 while (Parser
.Step(Section
) == true)
543 Progress
.Progress(Parser
.Offset());
545 string File
= Section
.FindS("Filename");
546 unsigned long Size
= Section
.FindI("Size");
547 if (File
.empty() || Size
== 0)
548 return _error
->Error("Cannot find filename or size tag");
551 File
= OrigPath
+ ChopDirs(File
,Chop
);
553 // See if the file exists
554 if (NoStat
== false || Hits
< 10)
556 // Attempt to fix broken structure
559 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
560 ReconstructChop(Chop
,*I
,File
) == false)
566 File
= OrigPath
+ ChopDirs(File
,Chop
);
571 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
578 if ((unsigned)Buf
.st_size
!= Size
)
588 // Copy it to the target package file
593 // Mangle the output filename
594 const char *Filename
;
595 Section
.Find("Filename",Filename
,Stop
);
597 /* We need to rewrite the filename field so we emit
598 all fields except the filename file and rewrite that one */
599 for (unsigned int I
= 0; I
!= Section
.Count(); I
++)
601 Section
.Get(Start
,Stop
,I
);
602 if (Start
<= Filename
&& Stop
> Filename
)
605 sprintf(S
,"Filename: %s\n",File
.c_str());
606 if (I
+ 1 == Section
.Count())
608 if (Target
.Write(S
,strlen(S
)) == false)
612 if (Target
.Write(Start
,Stop
-Start
) == false)
619 Section
.GetSection(Start
,Stop
);
620 if (Target
.Write(Start
,Stop
-Start
) == false)
626 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
628 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
630 // Move out of the partial directory
632 string FinalF
= _config
->FindDir("Dir::State::lists");
633 FinalF
+= URItoFileName(S
);
634 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
635 return _error
->Errno("rename","Failed to rename");
637 // Copy the release file
638 sprintf(S
,"cdrom:%s/%sRelease",Name
.c_str(),(*I
).c_str() + CDROM
.length());
639 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
640 TargetF
+= URItoFileName(S
);
641 if (FileExists(*I
+ "Release") == true)
643 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
644 FileFd
Rel(*I
+ "Release",FileFd::ReadOnly
);
645 if (_error
->PendingError() == true)
648 if (CopyFile(Rel
,Target
) == false)
653 // Empty release file
654 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
657 // Rename the release file
658 FinalF
= _config
->FindDir("Dir::State::lists");
659 FinalF
+= URItoFileName(S
);
660 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
661 return _error
->Errno("rename","Failed to rename");
664 /* Mangle the source to be in the proper notation with
665 prefix dist [component] */
666 *I
= string(*I
,Prefix
.length());
667 ConvertToSourceList(CDROM
,*I
);
668 *I
= Prefix
+ ' ' + *I
;
670 CurrentSize
+= Pkg
.Size();
675 cout
<< "Wrote " << Packages
<< " package records" ;
677 cout
<< " with " << NotFound
<< " missing files";
678 if (NotFound
!= 0 && WrongSize
!= 0)
681 cout
<< " with " << WrongSize
<< " mismatched files";
685 return _error
->Error("No valid package records were found.");
687 if (NotFound
+ WrongSize
> 10)
688 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
692 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
693 // ---------------------------------------------------------------------
694 /* This takes the list of source list expressed entires and collects
695 similar ones to form a single entry for each dist */
696 bool ReduceSourcelist(string CD
,vector
<string
> &List
)
698 sort(List
.begin(),List
.end());
700 // Collect similar entries
701 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
704 string::size_type Space
= (*I
).find(' ');
705 if (Space
== string::npos
)
707 string::size_type SSpace
= (*I
).find(' ',Space
+ 1);
708 if (SSpace
== string::npos
)
711 string Word1
= string(*I
,Space
,SSpace
-Space
);
712 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
715 string::size_type Space2
= (*J
).find(' ');
716 if (Space2
== string::npos
)
718 string::size_type SSpace2
= (*J
).find(' ',Space2
+ 1);
719 if (SSpace2
== string::npos
)
722 if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
)
725 *J
+= string(*I
,SSpace
);
730 // Wipe erased entries
731 for (unsigned int I
= 0; I
< List
.size();)
733 if (List
[I
].empty() == false)
736 List
.erase(List
.begin()+I
);
740 // WriteDatabase - Write the CDROM Database file /*{{{*/
741 // ---------------------------------------------------------------------
742 /* We rewrite the configuration class associated with the cdrom database. */
743 bool WriteDatabase(Configuration
&Cnf
)
745 string DFile
= _config
->FindFile("Dir::State::cdroms");
746 string NewFile
= DFile
+ ".new";
748 unlink(NewFile
.c_str());
749 ofstream
Out(NewFile
.c_str());
751 return _error
->Errno("ofstream::ofstream",
752 "Failed to open %s.new",DFile
.c_str());
754 /* Write out all of the configuration directives by walking the
755 configuration tree */
756 const Configuration::Item
*Top
= Cnf
.Tree(0);
759 // Print the config entry
760 if (Top
->Value
.empty() == false)
761 Out
<< Top
->FullTag() + " \"" << Top
->Value
<< "\";" << endl
;
769 while (Top
!= 0 && Top
->Next
== 0)
777 rename(DFile
.c_str(),string(DFile
+ '~').c_str());
778 if (rename(NewFile
.c_str(),DFile
.c_str()) != 0)
779 return _error
->Errno("rename","Failed to rename %s.new to %s",
780 DFile
.c_str(),DFile
.c_str());
785 // WriteSourceList - Write an updated sourcelist /*{{{*/
786 // ---------------------------------------------------------------------
787 /* This reads the old source list and copies it into the new one. It
788 appends the new CDROM entires just after the first block of comments.
789 This places them first in the file. It also removes any old entries
790 that were the same. */
791 bool WriteSourceList(string Name
,vector
<string
> &List
)
793 string File
= _config
->FindFile("Dir::Etc::sourcelist");
795 // Open the stream for reading
796 ifstream
F(File
.c_str(),ios::in
| ios::nocreate
);
798 return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str());
800 string NewFile
= File
+ ".new";
801 unlink(NewFile
.c_str());
802 ofstream
Out(NewFile
.c_str());
804 return _error
->Errno("ofstream::ofstream",
805 "Failed to open %s.new",File
.c_str());
807 // Create a short uri without the path
808 string ShortURI
= "cdrom:" + Name
+ "/";
813 while (F
.eof() == false)
815 F
.getline(Buffer
,sizeof(Buffer
));
817 _strtabexpand(Buffer
,sizeof(Buffer
));
821 if (Buffer
[0] == '#' || Buffer
[0] == 0)
823 Out
<< Buffer
<< endl
;
829 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
831 string::size_type Space
= (*I
).find(' ');
832 if (Space
== string::npos
)
833 return _error
->Error("Internal error");
835 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
836 "\" " << string(*I
,Space
+1) << endl
;
845 if (ParseQuoteWord(C
,Type
) == false ||
846 ParseQuoteWord(C
,URI
) == false)
848 Out
<< Buffer
<< endl
;
852 // Emit lines like this one
853 if (Type
!= "deb" || string(URI
,0,ShortURI
.length()) != ShortURI
)
855 Out
<< Buffer
<< endl
;
860 // Just in case the file was empty
863 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
865 string::size_type Space
= (*I
).find(' ');
866 if (Space
== string::npos
)
867 return _error
->Error("Internal error");
869 Out
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
870 "\" " << string(*I
,Space
+1) << endl
;
876 rename(File
.c_str(),string(File
+ '~').c_str());
877 if (rename(NewFile
.c_str(),File
.c_str()) != 0)
878 return _error
->Errno("rename","Failed to rename %s.new to %s",
879 File
.c_str(),File
.c_str());
885 // Prompt - Simple prompt /*{{{*/
886 // ---------------------------------------------------------------------
888 void Prompt(const char *Text
)
891 cout
<< Text
<< ' ' << flush
;
892 read(STDIN_FILENO
,&C
,1);
897 // PromptLine - Prompt for an input line /*{{{*/
898 // ---------------------------------------------------------------------
900 string
PromptLine(const char *Text
)
902 cout
<< Text
<< ':' << endl
;
910 // DoAdd - Add a new CDROM /*{{{*/
911 // ---------------------------------------------------------------------
912 /* This does the main add bit.. We show some status and things. The
913 sequence is to mount/umount the CD, Ident it then scan it for package
914 files and reduce that list. Then we copy over the package files and
915 verify them. Then rewrite the database files */
916 bool DoAdd(CommandLine
&)
919 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
920 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
923 Configuration Database
;
924 string DFile
= _config
->FindFile("Dir::State::cdroms");
925 if (FileExists(DFile
) == true)
927 if (ReadConfigFile(Database
,DFile
) == false)
928 return _error
->Error("Unable to read the cdrom database %s",
932 // Unmount the CD and get the user to put in the one they want
933 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
935 cout
<< "Unmounting CD-ROM" << endl
;
938 // Mount the new CDROM
939 Prompt("Please insert a Disc in the drive and press any key");
940 cout
<< "Mounting CD-ROM" << endl
;
941 if (MountCdrom(CDROM
) == false)
943 cout
<< "Failed to mount the cdrom." << endl
;
948 // Hash the CD to get an ID
949 cout
<< "Identifying.. " << flush
;
951 if (IdentCdrom(CDROM
,ID
) == false)
953 cout
<< '[' << ID
<< ']' << endl
;
955 cout
<< "Scanning Disc for index files.. " << flush
;
956 // Get the CD structure
958 string StartDir
= SafeGetCWD();
959 if (FindPackages(CDROM
,List
) == false)
961 chdir(StartDir
.c_str());
963 if (_config
->FindB("Debug::aptcdrom",false) == true)
965 cout
<< "I found:" << endl
;
966 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
973 DropBinaryArch(List
);
975 cout
<< "Found " << List
.size() << " package index files." << endl
;
977 if (List
.size() == 0)
978 return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
980 // Check if the CD is in the database
982 if (Database
.Exists("CD::" + ID
) == false ||
983 _config
->FindB("APT::CDROM::Rename",false) == true)
985 // Try to use the CDs label if at all possible
986 if (FileExists(CDROM
+ "/.disk/info") == true)
988 ifstream
F(string(CDROM
+ "/.disk/info").c_str());
992 if (Name
.empty() == false)
994 cout
<< "Found label '" << Name
<< "'" << endl
;
995 Database
.Set("CD::" + ID
+ "::Label",Name
);
999 if (_config
->FindB("APT::CDROM::Rename",false) == true ||
1000 Name
.empty() == true)
1002 cout
<< "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
1005 Name
= PromptLine("");
1006 if (Name
.empty() == false &&
1007 Name
.find('/') == string::npos
)
1009 cout
<< "That is not a valid name, try again " << endl
;
1015 Name
= Database
.Find("CD::" + ID
);
1016 Database
.Set("CD::" + ID
,Name
);
1017 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
1019 // Copy the package files to the state directory
1020 if (CopyPackages(CDROM
,Name
,List
) == false)
1023 ReduceSourcelist(CDROM
,List
);
1025 // Write the database and sourcelist
1026 if (_config
->FindB("APT::cdrom::NoAct",false) == false)
1028 if (WriteDatabase(Database
) == false)
1031 cout
<< "Writing new source list" << endl
;
1032 if (WriteSourceList(Name
,List
) == false)
1036 // Print the sourcelist entries
1037 cout
<< "Source List entires for this Disc are:" << endl
;
1038 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
1040 string::size_type Space
= (*I
).find(' ');
1041 if (Space
== string::npos
)
1042 return _error
->Error("Internal error");
1044 cout
<< "deb \"cdrom:" << Name
<< "/" << string(*I
,0,Space
) <<
1045 "\" " << string(*I
,Space
+1) << endl
;
1052 // ShowHelp - Show the help screen /*{{{*/
1053 // ---------------------------------------------------------------------
1057 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
1058 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
1060 cout
<< "Usage: apt-cdrom [options] command" << endl
;
1062 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
1063 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
1064 cout
<< "and /etc/fstab." << endl
;
1066 cout
<< "Commands:" << endl
;
1067 cout
<< " add - Add a CDROM" << endl
;
1069 cout
<< "Options:" << endl
;
1070 cout
<< " -h This help text" << endl
;
1071 cout
<< " -d CD-ROM mount point" << endl
;
1072 cout
<< " -r Rename a recognized CD-ROM" << endl
;
1073 cout
<< " -m No mounting" << endl
;
1074 cout
<< " -f Fast mode, don't check package files" << endl
;
1075 cout
<< " -c=? Read this configuration file" << endl
;
1076 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
1077 cout
<< "See fstab(5)" << endl
;
1082 int main(int argc
,const char *argv
[])
1084 CommandLine::Args Args
[] = {
1085 {'h',"help","help",0},
1086 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
1087 {'r',"rename","APT::CDROM::Rename",0},
1088 {'m',"no-mount","APT::CDROM::NoMount",0},
1089 {'f',"fast","APT::CDROM::Fast",0},
1090 {'n',"just-print","APT::CDROM::NoAct",0},
1091 {'n',"recon","APT::CDROM::NoAct",0},
1092 {'n',"no-act","APT::CDROM::NoAct",0},
1093 {'c',"config-file",0,CommandLine::ConfigFile
},
1094 {'o',"option",0,CommandLine::ArbItem
},
1096 CommandLine::Dispatch Cmds
[] = {
1100 // Parse the command line and initialize the package library
1101 CommandLine
CmdL(Args
,_config
);
1102 if (pkgInitialize(*_config
) == false ||
1103 CmdL
.Parse(argc
,argv
) == false)
1105 _error
->DumpErrors();
1109 // See if the help should be shown
1110 if (_config
->FindB("help") == true ||
1111 CmdL
.FileSize() == 0)
1114 // Match the operation
1115 CmdL
.DispatchArg(Cmds
);
1117 // Print any errors or warnings found during parsing
1118 if (_error
->empty() == false)
1120 bool Errors
= _error
->PendingError();
1121 _error
->DumpErrors();
1122 return Errors
== true?100:0;