- move yet more easyish cases of common apt-get/shell code to cmdline
[apt.git] / cmdline / cmdline.cc
1 // -*- mode: c++; mode: fold -*-
2 #include <apt-pkg/configuration.h>
3 #include <apt-pkg/error.h>
4 #include <apt-pkg/acquire.h>
5
6 #include <config.h>
7 #include <apti18n.h>
8
9 #include "cmdline.h"
10
11 #include <regex.h>
12 #include <sys/stat.h>
13
14 using namespace std;
15
16 // YnPrompt - Yes No Prompt.                                            /*{{{*/
17 // ---------------------------------------------------------------------
18 /* Returns true on a Yes.*/
19 bool YnPrompt()
20 {
21    if (_config->FindB("APT::Get::Assume-Yes",false) == true)
22    {
23       c1out << _("Y") << endl;
24       return true;
25    }
26
27    char response[1024] = "";
28    cin.getline(response, sizeof(response));
29
30    if (!cin)
31       return false;
32
33    if (strlen(response) == 0)
34       return true;
35
36    regex_t Pattern;
37    int Res;
38
39    Res = regcomp(&Pattern, nl_langinfo(YESEXPR),
40                  REG_EXTENDED|REG_ICASE|REG_NOSUB);
41
42    if (Res != 0) {
43       char Error[300];        
44       regerror(Res,&Pattern,Error,sizeof(Error));
45       return _error->Error(_("Regex compilation error - %s"),Error);
46    }
47    
48    Res = regexec(&Pattern, response, 0, NULL, 0);
49    if (Res == 0)
50       return true;
51    return false;
52 }
53                                                                         /*}}}*/
54 // AnalPrompt - Annoying Yes No Prompt.                                 /*{{{*/
55 // ---------------------------------------------------------------------
56 /* Returns true on a Yes.*/
57 bool AnalPrompt(const char *Text)
58 {
59    char Buf[1024];
60    cin.getline(Buf,sizeof(Buf));
61    if (strcmp(Buf,Text) == 0)
62       return true;
63    return false;
64 }
65                                                                         /*}}}*/
66 // ShowList - Show a list                                               /*{{{*/
67 // ---------------------------------------------------------------------
68 /* This prints out a string of space separated words with a title and 
69    a two space indent line wraped to the current screen width. */
70 bool ShowList(ostream &out,string Title,string List,string VersionsList)
71 {
72    if (List.empty() == true)
73       return true;
74    // trim trailing space
75    int NonSpace = List.find_last_not_of(' ');
76    if (NonSpace != -1)
77    {
78       List = List.erase(NonSpace + 1);
79       if (List.empty() == true)
80          return true;
81    }
82
83    // Acount for the leading space
84    int ScreenWidth = ::ScreenWidth - 3;
85       
86    out << Title << endl;
87    string::size_type Start = 0;
88    string::size_type VersionsStart = 0;
89    while (Start < List.size())
90    {
91       if(_config->FindB("APT::Get::Show-Versions",false) == true &&
92          VersionsList.size() > 0) {
93          string::size_type End;
94          string::size_type VersionsEnd;
95          
96          End = List.find(' ',Start);
97          VersionsEnd = VersionsList.find('\n', VersionsStart);
98
99          out << "   " << string(List,Start,End - Start) << " (" << 
100             string(VersionsList,VersionsStart,VersionsEnd - VersionsStart) << 
101             ")" << endl;
102
103          if (End == string::npos || End < Start)
104             End = Start + ScreenWidth;
105
106          Start = End + 1;
107          VersionsStart = VersionsEnd + 1;
108       } else {
109          string::size_type End;
110
111          if (Start + ScreenWidth >= List.size())
112             End = List.size();
113          else
114             End = List.rfind(' ',Start+ScreenWidth);
115
116          if (End == string::npos || End < Start)
117             End = Start + ScreenWidth;
118          out << "  " << string(List,Start,End - Start) << endl;
119          Start = End + 1;
120       }
121    }   
122
123    return false;
124 }
125
126 const char *op2str(int op)
127 {
128    switch (op & 0x0f)
129    {
130       case pkgCache::Dep::LessEq: return "<=";
131       case pkgCache::Dep::GreaterEq: return ">=";
132       case pkgCache::Dep::Less: return "<";
133       case pkgCache::Dep::Greater: return ">";
134       case pkgCache::Dep::Equals: return "=";
135       case pkgCache::Dep::NotEquals: return "!";
136       default: return "";
137    }
138 }
139
140 // SigWinch - Window size change signal handler                         /*{{{*/
141 // ---------------------------------------------------------------------
142 /* */
143 void SigWinch(int)
144 {
145    // Riped from GNU ls
146 #ifdef TIOCGWINSZ
147    struct winsize ws;
148
149    if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col >= 5)
150       ScreenWidth = ws.ws_col - 1;
151 #endif
152 }
153
154 // CacheFile::NameComp - QSort compare by name                          /*{{{*/
155 // ---------------------------------------------------------------------
156 /* */
157 pkgCache *cmdCacheFile::SortCache = 0;
158 int cmdCacheFile::NameComp(const void *a,const void *b)
159 {
160    if (*(pkgCache::Package **)a == 0 || *(pkgCache::Package **)b == 0)
161       return *(pkgCache::Package **)a - *(pkgCache::Package **)b;
162
163    const pkgCache::Package &A = **(pkgCache::Package **)a;
164    const pkgCache::Package &B = **(pkgCache::Package **)b;
165
166    return strcmp(SortCache->StrP + A.Name,SortCache->StrP + B.Name);
167 }
168                                                                         /*}}}*/
169 // CacheFile::Sort - Sort by name                                       /*{{{*/
170 // ---------------------------------------------------------------------
171 /* */
172 void cmdCacheFile::Sort()
173 {
174    delete [] List;
175    List = new pkgCache::Package *[Cache->Head().PackageCount];
176    memset(List,0,sizeof(*List)*Cache->Head().PackageCount);
177    pkgCache::PkgIterator I = Cache->PkgBegin();
178    for (;I.end() != true; I++)
179       List[I->ID] = I;
180
181    SortCache = *this;
182    qsort(List,Cache->Head().PackageCount,sizeof(*List),NameComp);
183 }
184
185 // ShowBroken - Debugging aide                                          /*{{{*/
186 // ---------------------------------------------------------------------
187 /* This prints out the names of all the packages that are broken along
188    with the name of each each broken dependency and a quite version 
189    description.
190    
191    The output looks like:
192  The following packages have unmet dependencies:
193      exim: Depends: libc6 (>= 2.1.94) but 2.1.3-10 is to be installed
194            Depends: libldap2 (>= 2.0.2-2) but it is not going to be installed
195            Depends: libsasl7 but it is not going to be installed   
196  */
197 void ShowBroken(ostream &out,cmdCacheFile &Cache,bool Now,pkgDepCache::State *State)
198 {
199    out << _("The following packages have unmet dependencies:") << endl;
200    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
201    {
202       pkgCache::PkgIterator I(Cache,Cache.List[J]);
203       
204       if (Now == true)
205       {
206          if (Cache[I].NowBroken() == false)
207             continue;
208       }
209       else
210       {
211          if (Cache[I].InstBroken() == false)
212             continue;
213       }
214       
215       // Print out each package and the failed dependencies
216       out <<"  " <<  I.Name() << ":";
217       size_t Indent = strlen(I.Name()) + 3;
218       bool First = true;
219       pkgCache::VerIterator Ver;
220       
221       if (Now == true)
222          Ver = I.CurrentVer();
223       else
224          Ver = Cache[I].InstVerIter(Cache);
225       
226       if (Ver.end() == true)
227       {
228          out << endl;
229          continue;
230       }
231       
232       for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false;)
233       {
234          // Compute a single dependency element (glob or)
235          pkgCache::DepIterator Start;
236          pkgCache::DepIterator End;
237          D.GlobOr(Start,End);
238
239          // CNC:2003-02-22 - IsImportantDep() currently calls IsCritical(), so
240          //                  these two are currently doing the same thing. Check
241          //                  comments in IsImportantDep() definition.
242 #if 0
243          if (Cache->IsImportantDep(End) == false)
244             continue;
245 #else
246          if (End.IsCritical() == false)
247             continue;
248 #endif
249          
250          if (Now == true)
251          {
252             if ((Cache[End] & pkgDepCache::DepGNow) == pkgDepCache::DepGNow)
253                continue;
254          }
255          else
256          {
257             if ((Cache[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall)
258                continue;
259          }
260          
261          bool FirstOr = true;
262          while (1)
263          {
264             if (First == false)
265                for (unsigned J = 0; J != Indent; J++)
266                   out << ' ';
267             First = false;
268
269             if (FirstOr == false)
270             {
271                for (size_t J = 0; J != strlen(End.DepType()) + 3; J++)
272                   out << ' ';
273             }
274             else
275                out << ' ' << End.DepType() << ": ";
276             FirstOr = false;
277             
278             out << Start.TargetPkg().Name();
279          
280             // Show a quick summary of the version requirements
281             if (Start.TargetVer() != 0)
282                out << " (" << Start.CompType() << " " << Start.TargetVer() << ")";
283             
284             /* Show a summary of the target package if possible. In the case
285                of virtual packages we show nothing */    
286             pkgCache::PkgIterator Targ = Start.TargetPkg();
287             if (Targ->ProvidesList == 0)
288             {
289                out << ' ';
290                pkgCache::VerIterator Ver = Cache[Targ].InstVerIter(Cache);
291                if (Now == true)
292                   Ver = Targ.CurrentVer();
293                     
294                if (Ver.end() == false)
295                {
296                   if (Now == true)
297                      ioprintf(out,_("but %s is installed"),Ver.VerStr());
298                   else
299                      ioprintf(out,_("but %s is to be installed"),Ver.VerStr());
300                }               
301                else
302                {
303                   if (Cache[Targ].CandidateVerIter(Cache).end() == true)
304                   {
305                      if (Targ->ProvidesList == 0)
306                         out << _("but it is not installable");
307                      else
308                         out << _("but it is a virtual package");
309                   }               
310                   else
311                      out << (Now?_("but it is not installed"):_("but it is not going to be installed"));
312                }               
313             }
314             
315             if (Start != End)
316                out << _(" or");
317             out << endl;
318             
319             if (Start == End)
320                break;
321             Start++;
322          }       
323       }     
324    }   
325 }
326                                                                         /*}}}*/
327 // ShowNew - Show packages to newly install                             /*{{{*/
328 // ---------------------------------------------------------------------
329 /* */
330 void ShowNew(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
331 {
332    /* Print out a list of packages that are going to be installed extra
333       to what the user asked */
334    string List;
335    string VersionsList;
336    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
337    {
338       pkgCache::PkgIterator I(Cache,Cache.List[J]);
339       if (Cache[I].NewInstall() == true &&
340           (State == NULL || (*State)[I].NewInstall() == false)) {
341          List += string(I.Name()) + " ";
342          VersionsList += string(Cache[I].CandVersion) + "\n";
343       }
344    }
345    
346    ShowList(out,_("The following NEW packages will be installed:"),List,VersionsList);
347 }
348                                                                         /*}}}*/
349 // ShowDel - Show packages to delete                                    /*{{{*/
350 // ---------------------------------------------------------------------
351 /* */
352 void ShowDel(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
353 {
354    /* Print out a list of packages that are going to be removed extra
355       to what the user asked */
356    string List, RepList; // CNC:2002-07-25
357    string VersionsList;
358    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
359    {
360       pkgCache::PkgIterator I(Cache,Cache.List[J]);
361       if (Cache[I].Delete() == true &&
362           (State == NULL || (*State)[I].Delete() == false))
363       {
364          // CNC:2002-07-25
365          bool Obsoleted = false;
366          string by;
367          for (pkgCache::DepIterator D = I.RevDependsList(); D.end() == false; D++)
368          {
369             if (D->Type == pkgCache::Dep::Obsoletes &&
370                 Cache[D.ParentPkg()].Install() &&
371                 (pkgCache::Version*)D.ParentVer() == Cache[D.ParentPkg()].InstallVer &&
372                 Cache->VS().CheckDep(I.CurrentVer().VerStr(), D) == true)
373             {
374                if (Obsoleted)
375                   by += ", " + string(D.ParentPkg().Name());
376                else
377                {
378                   Obsoleted = true;
379                   by = D.ParentPkg().Name();
380                }
381             }
382          }
383          if (Obsoleted)
384             RepList += string(I.Name()) + " (by " + by + ")  ";
385          else
386          {
387             if ((Cache[I].iFlags & pkgDepCache::Purge) == pkgDepCache::Purge)
388                List += string(I.Name()) + "* ";
389             else
390                List += string(I.Name()) + " ";
391          }
392      
393      // CNC:2004-03-09
394      VersionsList += string(I.CurrentVer().VerStr())+ "\n";
395       }
396    }
397    
398    // CNC:2002-07-25
399    ShowList(out,_("The following packages will be REPLACED:"),RepList,VersionsList);
400    ShowList(out,_("The following packages will be REMOVED:"),List,VersionsList);
401 }
402                                                                         /*}}}*/
403 // ShowKept - Show kept packages                                        /*{{{*/
404 // ---------------------------------------------------------------------
405 /* */
406 void ShowKept(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
407 {
408    string List;
409    string VersionsList;
410    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
411    {     
412       pkgCache::PkgIterator I(Cache,Cache.List[J]);
413       
414       if (State == NULL) {
415          // Not interesting
416          if (Cache[I].Upgrade() == true || Cache[I].Upgradable() == false ||
417              I->CurrentVer == 0 || Cache[I].Delete() == true)
418             continue;
419       } else {
420          // Not interesting
421          if (!((Cache[I].Install() == false && (*State)[I].Install() == true) ||
422                (Cache[I].Delete() == false && (*State)[I].Delete() == true)))
423             continue;
424       }
425       
426       List += string(I.Name()) + " ";
427       VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
428    }
429    ShowList(out,_("The following packages have been kept back"),List,VersionsList);
430 }
431                                                                         /*}}}*/
432 // ShowUpgraded - Show upgraded packages                                /*{{{*/
433 // ---------------------------------------------------------------------
434 /* */
435 void ShowUpgraded(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
436 {
437    string List;
438    string VersionsList;
439    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
440    {
441       pkgCache::PkgIterator I(Cache,Cache.List[J]);
442       
443       if (State == NULL) {
444          // Not interesting
445          if (Cache[I].Upgrade() == false || Cache[I].NewInstall() == true)
446             continue;
447       } else {
448          // Not interesting
449          if (Cache[I].NewInstall() == true ||
450              !(Cache[I].Upgrade() == true && (*State)[I].Upgrade() == false))
451             continue;
452       }
453       
454       List += string(I.Name()) + " ";
455       VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
456    }
457    ShowList(out,_("The following packages will be upgraded"),List,VersionsList);
458 }
459                                                                         /*}}}*/
460 // ShowDowngraded - Show downgraded packages                            /*{{{*/
461 // ---------------------------------------------------------------------
462 /* */
463 bool ShowDowngraded(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
464 {
465    string List;
466    string VersionsList;
467    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
468    {
469       pkgCache::PkgIterator I(Cache,Cache.List[J]);
470       
471       if (State == NULL) {
472          // Not interesting
473          if (Cache[I].Downgrade() == false || Cache[I].NewInstall() == true)
474             continue;
475       } else {
476          // Not interesting
477          if (Cache[I].NewInstall() == true ||
478              !(Cache[I].Downgrade() == true && (*State)[I].Downgrade() == false))
479             continue;
480       }
481       
482       List += string(I.Name()) + " ";
483       VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
484    }
485    return ShowList(out,_("The following packages will be DOWNGRADED"),List,VersionsList);
486 }
487                                                                         /*}}}*/
488 // ShowHold - Show held but changed packages                            /*{{{*/
489 // ---------------------------------------------------------------------
490 /* */
491 bool ShowHold(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
492 {
493    string List;
494    string VersionsList;
495    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
496    {
497       pkgCache::PkgIterator I(Cache,Cache.List[J]);
498       if (Cache[I].InstallVer != (pkgCache::Version *)I.CurrentVer() &&
499           I->SelectedState == pkgCache::State::Hold &&
500           (State == NULL ||
501            Cache[I].InstallVer != (*State)[I].InstallVer)) {
502          List += string(I.Name()) + " ";
503          VersionsList += string(Cache[I].CurVersion) + " => " + Cache[I].CandVersion + "\n";
504       }
505    }
506
507    return ShowList(out,_("The following held packages will be changed:"),List,VersionsList);
508 }
509                                                                         /*}}}*/
510 // ShowEssential - Show an essential package warning                    /*{{{*/
511 // ---------------------------------------------------------------------
512 /* This prints out a warning message that is not to be ignored. It shows
513    all essential packages and their dependents that are to be removed. 
514    It is insanely risky to remove the dependents of an essential package! */
515 bool ShowEssential(ostream &out,cmdCacheFile &Cache,pkgDepCache::State *State)
516 {
517    string List;
518    string VersionsList;
519    bool *Added = new bool[Cache->Head().PackageCount];
520    for (unsigned int I = 0; I != Cache->Head().PackageCount; I++)
521       Added[I] = false;
522    
523    for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
524    {
525       pkgCache::PkgIterator I(Cache,Cache.List[J]);
526       if ((I->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential &&
527           (I->Flags & pkgCache::Flag::Important) != pkgCache::Flag::Important)
528          continue;
529       
530       // The essential package is being removed
531       if (Cache[I].Delete() == true &&
532           (State == NULL || (*State)[I].Delete() == false))
533       {
534          if (Added[I->ID] == false)
535          {
536             // CNC:2003-03-21 - Do not consider a problem if that package is being obsoleted
537             //                  by something else.
538             bool Obsoleted = false;
539             for (pkgCache::DepIterator D = I.RevDependsList(); D.end() == false; D++)
540             {
541                if (D->Type == pkgCache::Dep::Obsoletes &&
542                    Cache[D.ParentPkg()].Install() &&
543                    ((pkgCache::Version*)D.ParentVer() == Cache[D.ParentPkg()].InstallVer ||
544                     (pkgCache::Version*)D.ParentVer() == ((pkgCache::Version*)D.ParentPkg().CurrentVer())) &&
545                    Cache->VS().CheckDep(I.CurrentVer().VerStr(), D) == true)
546                {
547                   Obsoleted = true;
548                   break;
549                }
550             }
551             if (Obsoleted == false) {
552                Added[I->ID] = true;
553                List += string(I.Name()) + " ";
554             }
555         //VersionsList += string(Cache[I].CurVersion) + "\n"; ???
556          }
557       }
558       
559       if (I->CurrentVer == 0)
560          continue;
561
562       // Print out any essential package depenendents that are to be removed
563       for (pkgCache::DepIterator D = I.CurrentVer().DependsList(); D.end() == false; D++)
564       {
565          // Skip everything but depends
566          if (D->Type != pkgCache::Dep::PreDepends &&
567              D->Type != pkgCache::Dep::Depends)
568             continue;
569          
570          pkgCache::PkgIterator P = D.SmartTargetPkg();
571          if (Cache[P].Delete() == true &&
572              (State == NULL || (*State)[P].Delete() == false))
573          {
574             if (Added[P->ID] == true)
575                continue;
576
577             // CNC:2003-03-21 - Do not consider a problem if that package is being obsoleted
578             //                  by something else.
579             bool Obsoleted = false;
580             for (pkgCache::DepIterator D = P.RevDependsList(); D.end() == false; D++)
581             {
582                if (D->Type == pkgCache::Dep::Obsoletes &&
583                    Cache[D.ParentPkg()].Install() &&
584                    ((pkgCache::Version*)D.ParentVer() == Cache[D.ParentPkg()].InstallVer ||
585                     (pkgCache::Version*)D.ParentVer() == ((pkgCache::Version*)D.ParentPkg().CurrentVer())) &&
586                    Cache->VS().CheckDep(P.CurrentVer().VerStr(), D) == true)
587                {
588                   Obsoleted = true;
589                   break;
590                }
591             }
592             if (Obsoleted == true)
593                continue;
594
595             Added[P->ID] = true;
596             
597             char S[300];
598             snprintf(S,sizeof(S),_("%s (due to %s) "),P.Name(),I.Name());
599             List += S;
600         //VersionsList += "\n"; ???
601          }       
602       }      
603    }
604    
605    delete [] Added;
606    return ShowList(out,_("WARNING: The following essential packages will be removed\n"
607                          "This should NOT be done unless you know exactly what you are doing!"),List,VersionsList);
608 }
609                                                                         /*}}}*/
610 // Stats - Show some statistics                                         /*{{{*/
611 // ---------------------------------------------------------------------
612 /* */
613 void Stats(ostream &out,pkgDepCache &Dep,pkgDepCache::State *State)
614 {
615    unsigned long Upgrade = 0;
616    unsigned long Downgrade = 0;
617    unsigned long Install = 0;
618    unsigned long ReInstall = 0;
619    // CNC:2002-07-29
620    unsigned long Replace = 0;
621    unsigned long Remove = 0;
622    unsigned long Keep = 0;
623    for (pkgCache::PkgIterator I = Dep.PkgBegin(); I.end() == false; I++)
624    {
625       if (Dep[I].NewInstall() == true &&
626           (State == NULL || (*State)[I].NewInstall() == false))
627          Install++;
628       else
629       {
630          if (Dep[I].Upgrade() == true &&
631              (State == NULL || (*State)[I].Upgrade() == false))
632             Upgrade++;
633          else
634             if (Dep[I].Downgrade() == true &&
635                 (State == NULL || (*State)[I].Downgrade() == false))
636                Downgrade++;
637             else
638                if (State != NULL &&
639                    (((*State)[I].NewInstall() == true && Dep[I].NewInstall() == false) ||
640                     ((*State)[I].Upgrade() == true && Dep[I].Upgrade() == false) ||
641                     ((*State)[I].Downgrade() == true && Dep[I].Downgrade() == false)))
642                   Keep++;
643       }
644       // CNC:2002-07-29
645       if (Dep[I].Delete() == true &&
646           (State == NULL || (*State)[I].Delete() == false))
647       {
648          bool Obsoleted = false;
649          string by;
650          for (pkgCache::DepIterator D = I.RevDependsList();
651               D.end() == false; D++)
652          {
653             if (D->Type == pkgCache::Dep::Obsoletes &&
654                 Dep[D.ParentPkg()].Install() &&
655                 (pkgCache::Version*)D.ParentVer() == Dep[D.ParentPkg()].InstallVer &&
656                 Dep.VS().CheckDep(I.CurrentVer().VerStr(), D) == true)
657             {
658                Obsoleted = true;
659                break;
660             }
661          }
662          if (Obsoleted)
663             Replace++;
664          else
665             Remove++;
666       }
667       else if ((Dep[I].iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall &&
668                (State == NULL || !((*State)[I].iFlags & pkgDepCache::ReInstall)))
669          ReInstall++;
670    }   
671
672    ioprintf(out,_("%lu upgraded, %lu newly installed, "),
673             Upgrade,Install);
674    
675    if (ReInstall != 0)
676       ioprintf(out,_("%lu reinstalled, "),ReInstall);
677    if (Downgrade != 0)
678       ioprintf(out,_("%lu downgraded, "),Downgrade);
679    // CNC:2002-07-29
680    if (Replace != 0)
681       ioprintf(out,_("%lu replaced, "),Replace);
682
683    // CNC:2002-07-29
684    if (State == NULL)
685       ioprintf(out,_("%lu removed and %lu not upgraded.\n"),
686                Remove,Dep.KeepCount());
687    else
688       ioprintf(out,_("%lu removed and %lu kept.\n"),Remove,Keep);
689
690    
691    if (Dep.BadCount() != 0)
692       ioprintf(out,_("%lu not fully installed or removed.\n"),
693                Dep.BadCount());
694 }
695                                                                         /*}}}*/
696 bool cmdDoClean(CommandLine &CmdL)
697 {
698    if (_config->FindB("APT::Get::Simulate") == true)
699    {
700       cout << "Del " << _config->FindDir("Dir::Cache::archives") << "* " <<
701          _config->FindDir("Dir::Cache::archives") << "partial/*" << endl;
702       return true;
703    }
704
705    // Lock the archive directory
706    FileFd Lock;
707    if (_config->FindB("Debug::NoLocking",false) == false)
708    {
709       Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
710       if (_error->PendingError() == true)
711          return _error->Error(_("Unable to lock the download directory"));
712    }
713
714    pkgAcquire Fetcher;
715    Fetcher.Clean(_config->FindDir("Dir::Cache::archives"));
716    Fetcher.Clean(_config->FindDir("Dir::Cache::archives") + "partial/");
717    return true;
718 }
719
720 // FindSrc - Find a source record                                       /*{{{*/
721 // ---------------------------------------------------------------------
722 /* */
723 pkgSrcRecords::Parser *FindSrc(const char *Name,pkgRecords &Recs,
724                                pkgSrcRecords &SrcRecs,string &Src,
725                                pkgDepCache &Cache)
726 {
727    // We want to pull the version off the package specification..
728    string VerTag;
729    string TmpSrc = Name;
730    string::size_type Slash = TmpSrc.rfind('=');
731    if (Slash != string::npos)
732    {
733       VerTag = string(TmpSrc.begin() + Slash + 1,TmpSrc.end());
734       TmpSrc = string(TmpSrc.begin(),TmpSrc.begin() + Slash);
735    }
736    
737    /* Lookup the version of the package we would install if we were to
738       install a version and determine the source package name, then look
739       in the archive for a source package of the same name. In theory
740       we could stash the version string as well and match that too but
741       today there aren't multi source versions in the archive. */
742    if (_config->FindB("APT::Get::Only-Source") == false && 
743        VerTag.empty() == true)
744    {
745       pkgCache::PkgIterator Pkg = Cache.FindPkg(TmpSrc);
746       if (Pkg.end() == false)
747       {
748          pkgCache::VerIterator Ver = Cache.GetCandidateVer(Pkg);      
749          if (Ver.end() == false)
750          {
751             pkgRecords::Parser &Parse = Recs.Lookup(Ver.FileList());
752             Src = Parse.SourcePkg();
753          }
754       }   
755    }
756    
757    // No source package name..
758    if (Src.empty() == true)
759       Src = TmpSrc;
760    
761    // The best hit
762    pkgSrcRecords::Parser *Last = 0;
763    unsigned long Offset = 0;
764    string Version;
765    bool IsMatch = false;
766    
767    // If we are matching by version then we need exact matches to be happy
768    if (VerTag.empty() == false)
769       IsMatch = true;
770    
771    /* Iterate over all of the hits, which includes the resulting
772       binary packages in the search */
773    pkgSrcRecords::Parser *Parse;
774    SrcRecs.Restart();
775    while ((Parse = SrcRecs.Find(Src.c_str(),false)) != 0)
776    {
777       string Ver = Parse->Version();
778       
779       // Skip name mismatches
780       if (IsMatch == true && Parse->Package() != Src)
781          continue;
782       
783       if (VerTag.empty() == false)
784       {
785          /* Don't want to fall through because we are doing exact version 
786             matching. */
787          if (Cache.VS().CmpVersion(VerTag,Ver) != 0)
788             continue;
789          
790          Last = Parse;
791          Offset = Parse->Offset();
792          break;
793       }
794                                   
795       // Newer version or an exact match
796       if (Last == 0 || Cache.VS().CmpVersion(Version,Ver) < 0 || 
797           (Parse->Package() == Src && IsMatch == false))
798       {
799          IsMatch = Parse->Package() == Src;
800          Last = Parse;
801          Offset = Parse->Offset();
802          Version = Ver;
803       }      
804    }
805    
806    if (Last == 0)
807       return 0;
808    
809    if (Last->Jump(Offset) == false)
810       return 0;
811    
812    return Last;
813 }
814                                                                         /*}}}*/
815 // DoAutoClean - Smartly remove downloaded archives                     /*{{{*/
816 // ---------------------------------------------------------------------
817 /* This is similar to clean but it only purges things that cannot be 
818    downloaded, that is old versions of cached packages. */
819 void LogCleaner::Erase(const char *File,string Pkg,string Ver,struct stat &St) 
820 {
821       c1out << "Del " << Pkg << " " << Ver << " [" << SizeToStr(St.st_size) << "B]" << endl;
822       
823       if (_config->FindB("APT::Get::Simulate") == false)
824          unlink(File);      
825 }
826
827 // vim:sts=3:sw=3