Fixed function return and moved cdrom stuff to lib
[ntk/apt.git] / cmdline / apt-cdrom.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: apt-cdrom.cc,v 1.6 1998/11/29 01:19:20 jgg Exp $
4 /* ######################################################################
5
6 APT CDROM - Tool for handling APT's CDROM database.
7
8 Currently the only option is 'add' which will take the current CD
9 in the drive and add it into the database.
10
11 ##################################################################### */
12 /*}}}*/
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 <strutl.h>
22 #include <config.h>
23
24 #include <iostream>
25 #include <fstream>
26 #include <vector>
27 #include <algorithm>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 /*}}}*/
34
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
40 binary-* sub dirs. */
41 bool FindPackages(string CD,vector<string> &List, int Depth = 0)
42 {
43 if (Depth >= 7)
44 return true;
45
46 if (CD[CD.length()-1] != '/')
47 CD += '/';
48
49 if (chdir(CD.c_str()) != 0)
50 return _error->Errno("chdir","Unable to change to %s",CD.c_str());
51
52 /* Aha! We found some package files. We assume that everything under
53 this dir is controlled by those package files so we don't look down
54 anymore */
55 struct stat Buf;
56 if (stat("Packages",&Buf) == 0 ||
57 stat("Packages.gz",&Buf) == 0)
58 {
59 List.push_back(CD);
60 return true;
61 }
62
63 DIR *D = opendir(".");
64 if (D == 0)
65 return _error->Errno("opendir","Unable to read %s",CD.c_str());
66
67 // Run over the directory
68 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
69 {
70 // Skip some files..
71 if (strcmp(Dir->d_name,".") == 0 ||
72 strcmp(Dir->d_name,"..") == 0 ||
73 strcmp(Dir->d_name,"source") == 0 ||
74 strcmp(Dir->d_name,"experimental") == 0 ||
75 strcmp(Dir->d_name,"binary-all") == 0)
76 continue;
77
78 // See if the name is a sub directory
79 struct stat Buf;
80 if (stat(Dir->d_name,&Buf) != 0)
81 continue;
82
83 if (S_ISDIR(Buf.st_mode) == 0)
84 continue;
85
86 // Descend
87 if (FindPackages(CD + Dir->d_name,List,Depth+1) == false)
88 break;
89
90 if (chdir(CD.c_str()) != 0)
91 return _error->Errno("chdir","Unable to change to ",CD.c_str());
92 };
93
94 closedir(D);
95
96 return !_error->PendingError();
97 }
98 /*}}}*/
99 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
100 // ---------------------------------------------------------------------
101 /* Here we drop everything that is not this machines arch */
102 bool DropBinaryArch(vector<string> &List)
103 {
104 char S[300];
105 sprintf(S,"/binary-%s/",_config->Find("Apt::Architecture").c_str());
106
107 for (unsigned int I = 0; I < List.size(); I++)
108 {
109 const char *Str = List[I].c_str();
110
111 const char *Res;
112 if ((Res = strstr(Str,"/binary-")) == 0)
113 continue;
114
115 // Weird, remove it.
116 if (strlen(Res) < strlen(S))
117 {
118 List.erase(List.begin() + I);
119 I--;
120 continue;
121 }
122
123 // See if it is our arch
124 if (stringcmp(Res,Res + strlen(S),S) == 0)
125 continue;
126
127 // Erase it
128 List.erase(List.begin() + I);
129 I--;
130 }
131
132 return true;
133 }
134 /*}}}*/
135 // Score - We compute a 'score' for a path /*{{{*/
136 // ---------------------------------------------------------------------
137 /* Paths are scored based on how close they come to what I consider
138 normal. That is ones that have 'dist' 'stable' 'frozen' will score
139 higher than ones without. */
140 int Score(string Path)
141 {
142 int Res = 0;
143 if (Path.find("stable/") != string::npos)
144 Res += 2;
145 if (Path.find("frozen/") != string::npos)
146 Res += 2;
147 if (Path.find("/dists/") != string::npos)
148 Res += 4;
149 if (Path.find("/main/") != string::npos)
150 Res += 2;
151 if (Path.find("/contrib/") != string::npos)
152 Res += 2;
153 if (Path.find("/non-free/") != string::npos)
154 Res += 2;
155 if (Path.find("/non-US/") != string::npos)
156 Res += 2;
157 return Res;
158 }
159 /*}}}*/
160 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
161 // ---------------------------------------------------------------------
162 /* Here we go and stat every file that we found and strip dup inodes. */
163 bool DropRepeats(vector<string> &List)
164 {
165 // Get a list of all the inodes
166 ino_t *Inodes = new ino_t[List.size()];
167 for (unsigned int I = 0; I != List.size(); I++)
168 {
169 struct stat Buf;
170 if (stat(List[I].c_str(),&Buf) != 0)
171 _error->Errno("stat","Failed to stat %s",List[I].c_str());
172 Inodes[I] = Buf.st_ino;
173 }
174
175 // Look for dups
176 for (unsigned int I = 0; I != List.size(); I++)
177 {
178 for (unsigned int J = I+1; J < List.size(); J++)
179 {
180 // No match
181 if (Inodes[J] != Inodes[I])
182 continue;
183
184 // We score the two paths.. and erase one
185 int ScoreA = Score(List[I]);
186 int ScoreB = Score(List[J]);
187 if (ScoreA < ScoreB)
188 {
189 List[I] = string();
190 break;
191 }
192
193 List[J] = string();
194 }
195 }
196
197 // Wipe erased entries
198 for (unsigned int I = 0; I < List.size();)
199 {
200 if (List[I].empty() == false)
201 I++;
202 else
203 List.erase(List.begin()+I);
204 }
205
206 return true;
207 }
208 /*}}}*/
209 // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
210 // ---------------------------------------------------------------------
211 /* We look for things in dists/ notation and convert them to
212 <dist> <component> form otherwise it is left alone. This also strips
213 the CD path. */
214 void ConvertToSourceList(string CD,string &Path)
215 {
216 char S[300];
217 sprintf(S,"binary-%s",_config->Find("Apt::Architecture").c_str());
218
219 // Strip the cdrom base path
220 Path = string(Path,CD.length());
221 if (Path.empty() == true)
222 Path = "/";
223
224 // Too short to be a dists/ type
225 if (Path.length() < strlen("dists/"))
226 return;
227
228 // Not a dists type.
229 if (stringcmp(Path.begin(),Path.begin()+strlen("dists/"),"dists/") != 0)
230 return;
231
232 // Isolate the dist
233 string::size_type Slash = strlen("dists/");
234 string::size_type Slash2 = Path.find('/',Slash + 1);
235 if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
236 return;
237 string Dist = string(Path,Slash,Slash2 - Slash);
238
239 // Isolate the component
240 Slash = Path.find('/',Slash2+1);
241 if (Slash == string::npos || Slash + 2 >= Path.length())
242 return;
243 string Comp = string(Path,Slash2+1,Slash - Slash2-1);
244
245 // Verify the trailing binar - bit
246 Slash2 = Path.find('/',Slash + 1);
247 if (Slash == string::npos)
248 return;
249 string Binary = string(Path,Slash+1,Slash2 - Slash-1);
250
251 if (Binary != S)
252 return;
253
254 Path = Dist + ' ' + Comp;
255 }
256 /*}}}*/
257 // GrabFirst - Return the first Depth path components /*{{{*/
258 // ---------------------------------------------------------------------
259 /* */
260 bool GrabFirst(string Path,string &To,unsigned int Depth)
261 {
262 string::size_type I = 0;
263 do
264 {
265 I = Path.find('/',I+1);
266 Depth--;
267 }
268 while (I != string::npos && Depth != 0);
269
270 if (I == string::npos)
271 return false;
272
273 To = string(Path,0,I+1);
274 return true;
275 }
276 /*}}}*/
277 // ChopDirs - Chop off the leading directory components /*{{{*/
278 // ---------------------------------------------------------------------
279 /* */
280 string ChopDirs(string Path,unsigned int Depth)
281 {
282 string::size_type I = 0;
283 do
284 {
285 I = Path.find('/',I+1);
286 Depth--;
287 }
288 while (I != string::npos && Depth != 0);
289
290 if (I == string::npos)
291 return string();
292
293 return string(Path,I+1);
294 }
295 /*}}}*/
296 // ReconstructPrefix - Fix strange prefixing /*{{{*/
297 // ---------------------------------------------------------------------
298 /* This prepends dir components from the path to the package files to
299 the path to the deb until it is found */
300 bool ReconstructPrefix(string &Prefix,string OrigPath,string CD,
301 string File)
302 {
303 bool Debug = _config->FindB("Debug::aptcdrom",false);
304 unsigned int Depth = 1;
305 string MyPrefix = Prefix;
306 while (1)
307 {
308 struct stat Buf;
309 if (stat(string(CD + MyPrefix + File).c_str(),&Buf) != 0)
310 {
311 if (Debug == true)
312 cout << "Failed, " << CD + MyPrefix + File << endl;
313 if (GrabFirst(OrigPath,MyPrefix,Depth++) == true)
314 continue;
315
316 return false;
317 }
318 else
319 {
320 Prefix = MyPrefix;
321 return true;
322 }
323 }
324 return false;
325 }
326 /*}}}*/
327 // ReconstructChop - Fixes bad source paths /*{{{*/
328 // ---------------------------------------------------------------------
329 /* This removes path components from the filename and prepends the location
330 of the package files until a file is found */
331 bool ReconstructChop(unsigned long &Chop,string Dir,string File)
332 {
333 // Attempt to reconstruct the filename
334 unsigned long Depth = 0;
335 while (1)
336 {
337 struct stat Buf;
338 if (stat(string(Dir + File).c_str(),&Buf) != 0)
339 {
340 File = ChopDirs(File,1);
341 Depth++;
342 if (File.empty() == false)
343 continue;
344 return false;
345 }
346 else
347 {
348 Chop = Depth;
349 return true;
350 }
351 }
352 return false;
353 }
354 /*}}}*/
355
356 // CopyPackages - Copy the package files from the CD /*{{{*/
357 // ---------------------------------------------------------------------
358 /* */
359 bool CopyPackages(string CDROM,string Name,vector<string> &List)
360 {
361 OpTextProgress Progress;
362
363 bool NoStat = _config->FindB("APT::CDROM::Fast",false);
364 bool Debug = _config->FindB("Debug::aptcdrom",false);
365
366 // Prepare the progress indicator
367 unsigned long TotalSize = 0;
368 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
369 {
370 struct stat Buf;
371 if (stat(string(*I + "Packages").c_str(),&Buf) != 0)
372 return _error->Errno("stat","Stat failed for %s",
373 string(*I + "Packages").c_str());
374 TotalSize += Buf.st_size;
375 }
376
377 unsigned long CurrentSize = 0;
378 unsigned int NotFound = 0;
379 unsigned int WrongSize = 0;
380 unsigned int Packages = 0;
381 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
382 {
383 string OrigPath = string(*I,CDROM.length());
384
385 // Open the package file
386 FileFd Pkg(*I + "Packages",FileFd::ReadOnly);
387 pkgTagFile Parser(Pkg);
388 if (_error->PendingError() == true)
389 return false;
390
391 // Open the output file
392 char S[400];
393 sprintf(S,"cdrom:%s/%sPackages",Name.c_str(),(*I).c_str() + CDROM.length());
394 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
395 TargetF += URItoFileName(S);
396 if (_config->FindB("APT::CDROM::NoAct",false) == true)
397 TargetF = "/dev/null";
398 FileFd Target(TargetF,FileFd::WriteEmpty);
399 if (_error->PendingError() == true)
400 return false;
401
402 // Setup the progress meter
403 Progress.OverallProgress(CurrentSize,TotalSize,Pkg.Size(),
404 "Reading Package Lists");
405
406 // Parse
407 Progress.SubProgress(Pkg.Size());
408 pkgTagSection Section;
409 string Prefix;
410 unsigned long Hits = 0;
411 unsigned long Chop = 0;
412 while (Parser.Step(Section) == true)
413 {
414 Progress.Progress(Parser.Offset());
415
416 string File = Section.FindS("Filename");
417 unsigned long Size = Section.FindI("Size");
418 if (File.empty() || Size == 0)
419 return _error->Error("Cannot find filename or size tag");
420
421 if (Chop != 0)
422 File = OrigPath + ChopDirs(File,Chop);
423
424 // See if the file exists
425 if (NoStat == false || Hits < 10)
426 {
427 // Attempt to fix broken structure
428 if (Hits == 0)
429 {
430 if (ReconstructPrefix(Prefix,OrigPath,CDROM,File) == false &&
431 ReconstructChop(Chop,*I,File) == false)
432 {
433 NotFound++;
434 continue;
435 }
436 if (Chop != 0)
437 File = OrigPath + ChopDirs(File,Chop);
438 }
439
440 // Get the size
441 struct stat Buf;
442 if (stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0)
443 {
444 NotFound++;
445 continue;
446 }
447
448 // Size match
449 if ((unsigned)Buf.st_size != Size)
450 {
451 WrongSize++;
452 continue;
453 }
454 }
455
456 Packages++;
457 Hits++;
458
459 // Copy it to the target package file
460 const char *Start;
461 const char *Stop;
462 if (Chop != 0)
463 {
464 // Mangle the output filename
465 const char *Filename;
466 Section.Find("Filename",Filename,Stop);
467
468 /* We need to rewrite the filename field so we emit
469 all fields except the filename file and rewrite that one */
470 for (unsigned int I = 0; I != Section.Count(); I++)
471 {
472 Section.Get(Start,Stop,I);
473 if (Start <= Filename && Stop > Filename)
474 {
475 char S[500];
476 sprintf(S,"Filename: %s\n",File.c_str());
477 if (I + 1 == Section.Count())
478 strcat(S,"\n");
479 if (Target.Write(S,strlen(S)) == false)
480 return false;
481 }
482 else
483 if (Target.Write(Start,Stop-Start) == false)
484 return false;
485 }
486 if (Target.Write("\n",1) == false)
487 return false;
488 }
489 else
490 {
491 Section.GetSection(Start,Stop);
492 if (Target.Write(Start,Stop-Start) == false)
493 return false;
494 }
495 }
496
497 if (Debug == true)
498 cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl;
499
500 if (_config->FindB("APT::CDROM::NoAct",false) == false)
501 {
502 // Move out of the partial directory
503 Target.Close();
504 string FinalF = _config->FindDir("Dir::State::lists");
505 FinalF += URItoFileName(S);
506 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
507 return _error->Errno("rename","Failed to rename");
508
509 // Copy the release file
510 sprintf(S,"cdrom:%s/%sRelease",Name.c_str(),(*I).c_str() + CDROM.length());
511 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
512 TargetF += URItoFileName(S);
513 if (FileExists(*I + "Release") == true)
514 {
515 FileFd Target(TargetF,FileFd::WriteEmpty);
516 FileFd Rel(*I + "Release",FileFd::ReadOnly);
517 if (_error->PendingError() == true)
518 return false;
519
520 if (CopyFile(Rel,Target) == false)
521 return false;
522 }
523 else
524 {
525 // Empty release file
526 FileFd Target(TargetF,FileFd::WriteEmpty);
527 }
528
529 // Rename the release file
530 FinalF = _config->FindDir("Dir::State::lists");
531 FinalF += URItoFileName(S);
532 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
533 return _error->Errno("rename","Failed to rename");
534 }
535
536 /* Mangle the source to be in the proper notation with
537 prefix dist [component] */
538 *I = string(*I,Prefix.length());
539 ConvertToSourceList(CDROM,*I);
540 *I = Prefix + ' ' + *I;
541
542 CurrentSize += Pkg.Size();
543 }
544 Progress.Done();
545
546 // Some stats
547 cout << "Wrote " << Packages << " package records" ;
548 if (NotFound != 0)
549 cout << " with " << NotFound << " missing files";
550 if (NotFound != 0 && WrongSize != 0)
551 cout << " and";
552 if (WrongSize != 0)
553 cout << " with " << WrongSize << " mismatched files";
554 cout << '.' << endl;
555
556 if (Packages == 0)
557 return _error->Error("No valid package records were found.");
558
559 if (NotFound + WrongSize > 10)
560 cout << "Alot of package entires were discarded, perhaps this CD is funny?" << endl;
561
562 return true;
563 }
564 /*}}}*/
565
566 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
567 // ---------------------------------------------------------------------
568 /* This takes the list of source list expressed entires and collects
569 similar ones to form a single entry for each dist */
570 bool ReduceSourcelist(string CD,vector<string> &List)
571 {
572 sort(List.begin(),List.end());
573
574 // Collect similar entries
575 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
576 {
577 // Find a space..
578 string::size_type Space = (*I).find(' ');
579 if (Space == string::npos)
580 continue;
581 string::size_type SSpace = (*I).find(' ',Space + 1);
582 if (SSpace == string::npos)
583 continue;
584
585 string Word1 = string(*I,Space,SSpace-Space);
586 for (vector<string>::iterator J = List.begin(); J != I; J++)
587 {
588 // Find a space..
589 string::size_type Space2 = (*J).find(' ');
590 if (Space2 == string::npos)
591 continue;
592 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
593 if (SSpace2 == string::npos)
594 continue;
595
596 if (string(*J,Space2,SSpace2-Space2) != Word1)
597 continue;
598
599 *J += string(*I,SSpace);
600 *I = string();
601 }
602 }
603
604 // Wipe erased entries
605 for (unsigned int I = 0; I < List.size();)
606 {
607 if (List[I].empty() == false)
608 I++;
609 else
610 List.erase(List.begin()+I);
611 }
612 }
613 /*}}}*/
614 // WriteDatabase - Write the CDROM Database file /*{{{*/
615 // ---------------------------------------------------------------------
616 /* We rewrite the configuration class associated with the cdrom database. */
617 bool WriteDatabase(Configuration &Cnf)
618 {
619 string DFile = _config->FindFile("Dir::State::cdroms");
620 string NewFile = DFile + ".new";
621
622 unlink(NewFile.c_str());
623 ofstream Out(NewFile.c_str());
624 if (!Out)
625 return _error->Errno("ofstream::ofstream",
626 "Failed to open %s.new",DFile.c_str());
627
628 /* Write out all of the configuration directives by walking the
629 configuration tree */
630 const Configuration::Item *Top = Cnf.Tree(0);
631 for (; Top != 0;)
632 {
633 // Print the config entry
634 if (Top->Value.empty() == false)
635 Out << Top->FullTag() + " \"" << Top->Value << "\";" << endl;
636
637 if (Top->Child != 0)
638 {
639 Top = Top->Child;
640 continue;
641 }
642
643 while (Top != 0 && Top->Next == 0)
644 Top = Top->Parent;
645 if (Top != 0)
646 Top = Top->Next;
647 }
648
649 Out.close();
650
651 rename(DFile.c_str(),string(DFile + '~').c_str());
652 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
653 return _error->Errno("rename","Failed to rename %s.new to %s",
654 DFile.c_str(),DFile.c_str());
655
656 return true;
657 }
658 /*}}}*/
659 // WriteSourceList - Write an updated sourcelist /*{{{*/
660 // ---------------------------------------------------------------------
661 /* This reads the old source list and copies it into the new one. It
662 appends the new CDROM entires just after the first block of comments.
663 This places them first in the file. It also removes any old entries
664 that were the same. */
665 bool WriteSourceList(string Name,vector<string> &List)
666 {
667 string File = _config->FindFile("Dir::Etc::sourcelist");
668
669 // Open the stream for reading
670 ifstream F(File.c_str(),ios::in | ios::nocreate);
671 if (!F != 0)
672 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
673
674 string NewFile = File + ".new";
675 unlink(NewFile.c_str());
676 ofstream Out(NewFile.c_str());
677 if (!Out)
678 return _error->Errno("ofstream::ofstream",
679 "Failed to open %s.new",File.c_str());
680
681 // Create a short uri without the path
682 string ShortURI = "cdrom:" + Name + "/";
683
684 char Buffer[300];
685 int CurLine = 0;
686 bool First = true;
687 while (F.eof() == false)
688 {
689 F.getline(Buffer,sizeof(Buffer));
690 CurLine++;
691 _strtabexpand(Buffer,sizeof(Buffer));
692 _strstrip(Buffer);
693
694 // Comment or blank
695 if (Buffer[0] == '#' || Buffer[0] == 0)
696 {
697 Out << Buffer << endl;
698 continue;
699 }
700
701 if (First == true)
702 {
703 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
704 {
705 string::size_type Space = (*I).find(' ');
706 if (Space == string::npos)
707 return _error->Error("Internal error");
708
709 Out << "deb \"cdrom:" << Name << "/" << string(*I,0,Space) <<
710 "\" " << string(*I,Space+1) << endl;
711 }
712 }
713 First = false;
714
715 // Grok it
716 string Type;
717 string URI;
718 char *C = Buffer;
719 if (ParseQuoteWord(C,Type) == false ||
720 ParseQuoteWord(C,URI) == false)
721 {
722 Out << Buffer << endl;
723 continue;
724 }
725
726 // Emit lines like this one
727 if (Type != "deb" || string(URI,0,ShortURI.length()) != ShortURI)
728 {
729 Out << Buffer << endl;
730 continue;
731 }
732 }
733
734 // Just in case the file was empty
735 if (First == true)
736 {
737 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
738 {
739 string::size_type Space = (*I).find(' ');
740 if (Space == string::npos)
741 return _error->Error("Internal error");
742
743 Out << "deb \"cdrom:" << Name << "/" << string(*I,0,Space) <<
744 "\" " << string(*I,Space+1) << endl;
745 }
746 }
747
748 Out.close();
749
750 rename(File.c_str(),string(File + '~').c_str());
751 if (rename(NewFile.c_str(),File.c_str()) != 0)
752 return _error->Errno("rename","Failed to rename %s.new to %s",
753 File.c_str(),File.c_str());
754
755 return true;
756 }
757 /*}}}*/
758
759 // Prompt - Simple prompt /*{{{*/
760 // ---------------------------------------------------------------------
761 /* */
762 void Prompt(const char *Text)
763 {
764 char C;
765 cout << Text << ' ' << flush;
766 read(STDIN_FILENO,&C,1);
767 if (C != '\n')
768 cout << endl;
769 }
770 /*}}}*/
771 // PromptLine - Prompt for an input line /*{{{*/
772 // ---------------------------------------------------------------------
773 /* */
774 string PromptLine(const char *Text)
775 {
776 cout << Text << ':' << endl;
777
778 string Res;
779 getline(cin,Res);
780 return Res;
781 }
782 /*}}}*/
783
784 // DoAdd - Add a new CDROM /*{{{*/
785 // ---------------------------------------------------------------------
786 /* This does the main add bit.. We show some status and things. The
787 sequence is to mount/umount the CD, Ident it then scan it for package
788 files and reduce that list. Then we copy over the package files and
789 verify them. Then rewrite the database files */
790 bool DoAdd(CommandLine &)
791 {
792 // Startup
793 string CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
794 cout << "Using CD-ROM mount point " << CDROM << endl;
795
796 // Read the database
797 Configuration Database;
798 string DFile = _config->FindFile("Dir::State::cdroms");
799 if (FileExists(DFile) == true)
800 {
801 if (ReadConfigFile(Database,DFile) == false)
802 return _error->Error("Unable to read the cdrom database %s",
803 DFile.c_str());
804 }
805
806 // Unmount the CD and get the user to put in the one they want
807 if (_config->FindB("APT::CDROM::NoMount",false) == false)
808 {
809 cout << "Unmounting CD-ROM" << endl;
810 UnmountCdrom(CDROM);
811
812 // Mount the new CDROM
813 Prompt("Please insert a Disc in the drive and press any key");
814 cout << "Mounting CD-ROM" << endl;
815 if (MountCdrom(CDROM) == false)
816 {
817 cout << "Failed to mount the cdrom." << endl;
818 return false;
819 }
820 }
821
822 // Hash the CD to get an ID
823 cout << "Identifying.. " << flush;
824 string ID;
825 if (IdentCdrom(CDROM,ID) == false)
826 return false;
827 cout << '[' << ID << ']' << endl;
828
829 cout << "Scanning Disc for index files.. " << flush;
830 // Get the CD structure
831 vector<string> List;
832 string StartDir = SafeGetCWD();
833 if (FindPackages(CDROM,List) == false)
834 return false;
835 chdir(StartDir.c_str());
836
837 if (_config->FindB("Debug::aptcdrom",false) == true)
838 {
839 cout << "I found:" << endl;
840 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
841 {
842 cout << *I << endl;
843 }
844 }
845
846 // Fix up the list
847 DropBinaryArch(List);
848 DropRepeats(List);
849 cout << "Found " << List.size() << " package index files." << endl;
850
851 if (List.size() == 0)
852 return _error->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
853
854 // Check if the CD is in the database
855 string Name;
856 if (Database.Exists("CD::" + ID) == false ||
857 _config->FindB("APT::CDROM::Rename",false) == true)
858 {
859 // Try to use the CDs label if at all possible
860 if (FileExists(CDROM + "/.disk/info") == true)
861 {
862 ifstream F(string(CDROM+ "/.disk/info").c_str());
863 if (!F == 0)
864 getline(F,Name);
865
866 if (Name.empty() == false)
867 {
868 cout << "Found label '" << Name << "'" << endl;
869 Database.Set("CD::" + ID + "::Label",Name);
870 }
871 }
872
873 if (_config->FindB("APT::CDROM::Rename",false) == true ||
874 Name.empty() == true)
875 {
876 cout << "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
877 while (1)
878 {
879 Name = PromptLine("");
880 if (Name.empty() == false &&
881 Name.find('/') == string::npos)
882 break;
883 cout << "That is not a valid name, try again " << endl;
884 }
885
886 }
887 }
888 else
889 Name = Database.Find("CD::" + ID);
890 Database.Set("CD::" + ID,Name);
891 cout << "This Disc is called '" << Name << "'" << endl;
892
893 // Copy the package files to the state directory
894 if (CopyPackages(CDROM,Name,List) == false)
895 return false;
896
897 ReduceSourcelist(CDROM,List);
898
899 // Write the database and sourcelist
900 if (_config->FindB("APT::cdrom::NoAct",false) == false)
901 {
902 if (WriteDatabase(Database) == false)
903 return false;
904
905 cout << "Writing new source list" << endl;
906 if (WriteSourceList(Name,List) == false)
907 return false;
908 }
909
910 // Print the sourcelist entries
911 cout << "Source List entires for this Disc are:" << endl;
912 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
913 {
914 string::size_type Space = (*I).find(' ');
915 if (Space == string::npos)
916 return _error->Error("Internal error");
917
918 cout << "deb \"cdrom:" << Name << "/" << string(*I,0,Space) <<
919 "\" " << string(*I,Space+1) << endl;
920 }
921
922 return true;
923 }
924 /*}}}*/
925
926 // ShowHelp - Show the help screen /*{{{*/
927 // ---------------------------------------------------------------------
928 /* */
929 int ShowHelp()
930 {
931 cout << PACKAGE << ' ' << VERSION << " for " << ARCHITECTURE <<
932 " compiled on " << __DATE__ << " " << __TIME__ << endl;
933
934 cout << "Usage: apt-cdrom [options] command" << endl;
935 cout << endl;
936 cout << "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl;
937 cout << "CDROM mount point and device information is taken from apt.conf" << endl;
938 cout << "and /etc/fstab." << endl;
939 cout << endl;
940 cout << "Commands:" << endl;
941 cout << " add - Add a CDROM" << endl;
942 cout << endl;
943 cout << "Options:" << endl;
944 cout << " -h This help text" << endl;
945 cout << " -d CD-ROM mount point" << endl;
946 cout << " -r Rename a recognized CD-ROM" << endl;
947 cout << " -m No mounting" << endl;
948 cout << " -f Fast mode, don't check package files" << endl;
949 cout << " -c=? Read this configuration file" << endl;
950 cout << " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl;
951 cout << "See fstab(5)" << endl;
952 return 100;
953 }
954 /*}}}*/
955
956 int main(int argc,const char *argv[])
957 {
958 CommandLine::Args Args[] = {
959 {'h',"help","help",0},
960 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg},
961 {'r',"rename","APT::CDROM::Rename",0},
962 {'m',"no-mount","APT::CDROM::NoMount",0},
963 {'f',"fast","APT::CDROM::Fast",0},
964 {'n',"just-print","APT::CDROM::NoAct",0},
965 {'n',"recon","APT::CDROM::NoAct",0},
966 {'n',"no-act","APT::CDROM::NoAct",0},
967 {'c',"config-file",0,CommandLine::ConfigFile},
968 {'o',"option",0,CommandLine::ArbItem},
969 {0,0,0,0}};
970 CommandLine::Dispatch Cmds[] = {
971 {"add",&DoAdd},
972 {0,0}};
973
974 // Parse the command line and initialize the package library
975 CommandLine CmdL(Args,_config);
976 if (pkgInitialize(*_config) == false ||
977 CmdL.Parse(argc,argv) == false)
978 {
979 _error->DumpErrors();
980 return 100;
981 }
982
983 // See if the help should be shown
984 if (_config->FindB("help") == true ||
985 CmdL.FileSize() == 0)
986 return ShowHelp();
987
988 // Match the operation
989 CmdL.DispatchArg(Cmds);
990
991 // Print any errors or warnings found during parsing
992 if (_error->empty() == false)
993 {
994 bool Errors = _error->PendingError();
995 _error->DumpErrors();
996 return Errors == true?100:0;
997 }
998
999 return 0;
1000 }