]> git.saurik.com Git - apt.git/blame - cmdline/apt-cdrom.cc
Fixed function return and moved cdrom stuff to lib
[apt.git] / cmdline / apt-cdrom.cc
CommitLineData
83d89a9f
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
65ae8fab 3// $Id: apt-cdrom.cc,v 1.6 1998/11/29 01:19:20 jgg Exp $
83d89a9f
AL
4/* ######################################################################
5
18444708
AL
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.
83d89a9f
AL
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
14#include <apt-pkg/cmndline.h>
15#include <apt-pkg/error.h>
16#include <apt-pkg/init.h>
83d89a9f
AL
17#include <apt-pkg/fileutl.h>
18#include <apt-pkg/progress.h>
19#include <apt-pkg/tagfile.h>
65ae8fab 20#include <apt-pkg/cdromutl.h>
83d89a9f
AL
21#include <strutl.h>
22#include <config.h>
23
24#include <iostream>
18444708 25#include <fstream>
83d89a9f
AL
26#include <vector>
27#include <algorithm>
83d89a9f
AL
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <dirent.h>
31#include <unistd.h>
32#include <stdio.h>
33 /*}}}*/
34
4dfaa253 35// FindPackages - Find the package files on the CDROM /*{{{*/
83d89a9f
AL
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. */
41bool FindPackages(string CD,vector<string> &List, int Depth = 0)
42{
4dfaa253 43 if (Depth >= 7)
83d89a9f
AL
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)
4dfaa253 81 continue;
83d89a9f
AL
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 /*}}}*/
83d89a9f
AL
99// DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
100// ---------------------------------------------------------------------
101/* Here we drop everything that is not this machines arch */
102bool 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. */
140int 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. */
163bool 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 /*}}}*/
4dfaa253 209// ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
83d89a9f 210// ---------------------------------------------------------------------
4dfaa253
AL
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. */
214void ConvertToSourceList(string CD,string &Path)
83d89a9f
AL
215{
216 char S[300];
217 sprintf(S,"binary-%s",_config->Find("Apt::Architecture").c_str());
4dfaa253
AL
218
219 // Strip the cdrom base path
220 Path = string(Path,CD.length());
3b7525a6
AL
221 if (Path.empty() == true)
222 Path = "/";
4dfaa253
AL
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;
83d89a9f 231
4dfaa253
AL
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);
83d89a9f 238
4dfaa253
AL
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);
83d89a9f 244
4dfaa253
AL
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);
83d89a9f 250
4dfaa253
AL
251 if (Binary != S)
252 return;
253
254 Path = Dist + ' ' + Comp;
255}
256 /*}}}*/
257// GrabFirst - Return the first Depth path components /*{{{*/
258// ---------------------------------------------------------------------
259/* */
260bool 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;
83d89a9f 272
4dfaa253
AL
273 To = string(Path,0,I+1);
274 return true;
275}
276 /*}}}*/
179ce12b
AL
277// ChopDirs - Chop off the leading directory components /*{{{*/
278// ---------------------------------------------------------------------
279/* */
280string 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 */
300bool 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 */
331bool 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
4dfaa253
AL
356// CopyPackages - Copy the package files from the CD /*{{{*/
357// ---------------------------------------------------------------------
358/* */
359bool CopyPackages(string CDROM,string Name,vector<string> &List)
360{
361 OpTextProgress Progress;
362
363 bool NoStat = _config->FindB("APT::CDROM::Fast",false);
179ce12b 364 bool Debug = _config->FindB("Debug::aptcdrom",false);
4dfaa253
AL
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 }
83d89a9f 376
4dfaa253
AL
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());
83d89a9f 384
4dfaa253
AL
385 // Open the package file
386 FileFd Pkg(*I + "Packages",FileFd::ReadOnly);
387 pkgTagFile Parser(Pkg);
388 if (_error->PendingError() == true)
389 return false;
83d89a9f 390
4dfaa253
AL
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;
83d89a9f 401
4dfaa253
AL
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;
179ce12b 411 unsigned long Chop = 0;
4dfaa253
AL
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
179ce12b
AL
421 if (Chop != 0)
422 File = OrigPath + ChopDirs(File,Chop);
423
4dfaa253
AL
424 // See if the file exists
425 if (NoStat == false || Hits < 10)
426 {
179ce12b
AL
427 // Attempt to fix broken structure
428 if (Hits == 0)
4dfaa253 429 {
179ce12b
AL
430 if (ReconstructPrefix(Prefix,OrigPath,CDROM,File) == false &&
431 ReconstructChop(Chop,*I,File) == false)
4dfaa253 432 {
4dfaa253 433 NotFound++;
179ce12b 434 continue;
4dfaa253 435 }
179ce12b
AL
436 if (Chop != 0)
437 File = OrigPath + ChopDirs(File,Chop);
4dfaa253
AL
438 }
439
179ce12b
AL
440 // Get the size
441 struct stat Buf;
442 if (stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0)
443 {
444 NotFound++;
4dfaa253 445 continue;
179ce12b
AL
446 }
447
4dfaa253
AL
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;
179ce12b
AL
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 }
3b7525a6
AL
486 if (Target.Write("\n",1) == false)
487 return false;
179ce12b
AL
488 }
489 else
490 {
491 Section.GetSection(Start,Stop);
492 if (Target.Write(Start,Stop-Start) == false)
493 return false;
494 }
4dfaa253
AL
495 }
496
179ce12b
AL
497 if (Debug == true)
498 cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl;
499
4dfaa253
AL
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;
65ae8fab
AL
561
562 return true;
4dfaa253
AL
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 */
570bool ReduceSourcelist(string CD,vector<string> &List)
571{
572 sort(List.begin(),List.end());
83d89a9f
AL
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;
4dfaa253
AL
581 string::size_type SSpace = (*I).find(' ',Space + 1);
582 if (SSpace == string::npos)
583 continue;
83d89a9f 584
4dfaa253 585 string Word1 = string(*I,Space,SSpace-Space);
83d89a9f
AL
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;
4dfaa253
AL
592 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
593 if (SSpace2 == string::npos)
594 continue;
83d89a9f 595
4dfaa253 596 if (string(*J,Space2,SSpace2-Space2) != Word1)
83d89a9f
AL
597 continue;
598
4dfaa253 599 *J += string(*I,SSpace);
83d89a9f
AL
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 /*}}}*/
18444708
AL
614// WriteDatabase - Write the CDROM Database file /*{{{*/
615// ---------------------------------------------------------------------
4dfaa253 616/* We rewrite the configuration class associated with the cdrom database. */
18444708
AL
617bool WriteDatabase(Configuration &Cnf)
618{
619 string DFile = _config->FindFile("Dir::State::cdroms");
4dfaa253
AL
620 string NewFile = DFile + ".new";
621
622 unlink(NewFile.c_str());
623 ofstream Out(NewFile.c_str());
18444708 624 if (!Out)
4dfaa253
AL
625 return _error->Errno("ofstream::ofstream",
626 "Failed to open %s.new",DFile.c_str());
18444708
AL
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());
4dfaa253 652 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
18444708
AL
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// ---------------------------------------------------------------------
4dfaa253
AL
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. */
18444708
AL
665bool WriteSourceList(string Name,vector<string> &List)
666{
4dfaa253
AL
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
18444708
AL
755 return true;
756}
757 /*}}}*/
83d89a9f
AL
758
759// Prompt - Simple prompt /*{{{*/
760// ---------------------------------------------------------------------
761/* */
762void 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/* */
774string 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// ---------------------------------------------------------------------
4dfaa253
AL
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 */
83d89a9f
AL
790bool 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);
18444708 811
83d89a9f 812 // Mount the new CDROM
18444708 813 Prompt("Please insert a Disc in the drive and press any key");
83d89a9f
AL
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
18444708 823 cout << "Identifying.. " << flush;
83d89a9f
AL
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());
4dfaa253
AL
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 }
83d89a9f
AL
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)
4dfaa253 852 return _error->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
83d89a9f
AL
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 {
4dfaa253
AL
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 ||
179ce12b 874 Name.empty() == true)
4dfaa253
AL
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 }
83d89a9f
AL
887 }
888 else
889 Name = Database.Find("CD::" + ID);
18444708 890 Database.Set("CD::" + ID,Name);
83d89a9f
AL
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
4dfaa253 897 ReduceSourcelist(CDROM,List);
18444708
AL
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;
4dfaa253
AL
904
905 cout << "Writing new source list" << endl;
906 if (WriteSourceList(Name,List) == false)
907 return false;
18444708 908 }
4dfaa253
AL
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
83d89a9f
AL
922 return true;
923}
924 /*}}}*/
925
926// ShowHelp - Show the help screen /*{{{*/
927// ---------------------------------------------------------------------
928/* */
929int 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;
18444708 948 cout << " -f Fast mode, don't check package files" << endl;
83d89a9f
AL
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
956int 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},
18444708
AL
964 {'n',"just-print","APT::CDROM::NoAct",0},
965 {'n',"recon","APT::CDROM::NoAct",0},
966 {'n',"no-act","APT::CDROM::NoAct",0},
83d89a9f
AL
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}