- initial import of revision 374 from cnc
[apt.git] / apt-pkg / policy.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: policy.cc,v 1.10 2003/08/12 00:17:37 mdz Exp $
4 /* ######################################################################
5
6    Package Version Policy implementation
7    
8    This is just a really simple wrapper around pkgVersionMatch with
9    some added goodies to manage the list of things..
10    
11    Priority Table:
12    
13    1000 -> inf = Downgradeable priorities
14    1000        = The 'no downgrade' pseduo-status file
15    100 -> 1000 = Standard priorities
16    990         = Config file override package files
17    989         = Start for preference auto-priorities
18    500         = Default package files
19    100         = The status file
20    0 -> 100    = NotAutomatic sources like experimental
21    -inf -> 0   = Never selected   
22    
23    ##################################################################### */
24                                                                         /*}}}*/
25 // Include Files                                                        /*{{{*/
26 #ifdef __GNUG__
27 #pragma implementation "apt-pkg/policy.h"
28 #endif
29 #include <apt-pkg/policy.h>
30 #include <apt-pkg/configuration.h>
31 #include <apt-pkg/tagfile.h>
32 #include <apt-pkg/strutl.h>
33 #include <apt-pkg/error.h>
34 #include <apt-pkg/sptr.h>
35     
36 #include <apti18n.h>
37
38 #include <iostream>
39                                                                         /*}}}*/
40
41 using namespace std;
42
43 // Policy::Init - Startup and bind to a cache                           /*{{{*/
44 // ---------------------------------------------------------------------
45 /* Set the defaults for operation. The default mode with no loaded policy
46    file matches the V0 policy engine. */
47 pkgPolicy::pkgPolicy(pkgCache *Owner) : Pins(0), PFPriority(0), Cache(Owner)
48 {
49    PFPriority = new signed short[Owner->Head().PackageFileCount];
50    Pins = new Pin[Owner->Head().PackageCount];
51
52    for (unsigned long I = 0; I != Owner->Head().PackageCount; I++)
53       Pins[I].Type = pkgVersionMatch::None;
54
55    // The config file has a master override.
56    string DefRel = _config->Find("APT::Default-Release");
57    if (DefRel.empty() == false)
58       CreatePin(pkgVersionMatch::Release,"",DefRel,990);
59       
60    InitDefaults();
61 }
62                                                                         /*}}}*/
63 // Policy::InitDefaults - Compute the default selections                /*{{{*/
64 // ---------------------------------------------------------------------
65 /* */
66 bool pkgPolicy::InitDefaults()
67 {   
68    // Initialize the priorities based on the status of the package file
69    for (pkgCache::PkgFileIterator I = Cache->FileBegin(); I != Cache->FileEnd(); I++)
70    {
71       PFPriority[I->ID] = 500;
72       if ((I->Flags & pkgCache::Flag::NotSource) == pkgCache::Flag::NotSource)
73          PFPriority[I->ID] = 100;
74       else
75          if ((I->Flags & pkgCache::Flag::NotAutomatic) == pkgCache::Flag::NotAutomatic)
76             PFPriority[I->ID] = 1;
77    }
78
79    // Apply the defaults..
80    SPtrArray<bool> Fixed = new bool[Cache->HeaderP->PackageFileCount];
81    memset(Fixed,0,sizeof(*Fixed)*Cache->HeaderP->PackageFileCount);
82    signed Cur = 989;
83    StatusOverride = false;
84    for (vector<Pin>::const_iterator I = Defaults.begin(); I != Defaults.end();
85         I++, Cur--)
86    {
87       pkgVersionMatch Match(I->Data,I->Type);
88       for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); F++)
89       {
90          if (Match.FileMatch(F) == true && Fixed[F->ID] == false)
91          {
92             if (I->Priority != 0 && I->Priority > 0)
93                Cur = I->Priority;
94             
95             if (I->Priority < 0)
96                PFPriority[F->ID] =  I->Priority;
97             else
98                PFPriority[F->ID] = Cur;
99             
100             if (PFPriority[F->ID] > 1000)
101                StatusOverride = true;
102             
103             Fixed[F->ID] = true;
104          }      
105       }      
106    }
107
108    if (_config->FindB("Debug::pkgPolicy",false) == true)
109       for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); F++)
110          cout << "Prio of " << F.FileName() << ' ' << PFPriority[F->ID] << endl; 
111    
112    return true;   
113 }
114                                                                         /*}}}*/
115 // Policy::GetCandidateVer - Get the candidate install version          /*{{{*/
116 // ---------------------------------------------------------------------
117 /* Evaluate the package pins and the default list to deteremine what the
118    best package is. */
119 pkgCache::VerIterator pkgPolicy::GetCandidateVer(pkgCache::PkgIterator Pkg)
120 {
121    // Look for a package pin and evaluate it.
122    signed Max = GetPriority(Pkg);
123    pkgCache::VerIterator Pref = GetMatch(Pkg);
124
125    /* Falling through to the default version.. Setting Max to zero
126       effectively excludes everything <= 0 which are the non-automatic
127       priorities.. The status file is given a prio of 100 which will exclude
128       not-automatic sources, except in a single shot not-installed mode.
129       The second pseduo-status file is at prio 1000, above which will permit
130       the user to force-downgrade things.
131       
132       The user pin is subject to the same priority rules as default 
133       selections. Thus there are two ways to create a pin - a pin that
134       tracks the default when the default is taken away, and a permanent
135       pin that stays at that setting.
136     */
137    for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; Ver++)
138    {
139       for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; VF++)
140       {
141          /* If this is the status file, and the current version is not the
142             version in the status file (ie it is not installed, or somesuch)
143             then it is not a candidate for installation, ever. This weeds
144             out bogus entries that may be due to config-file states, or
145             other. */
146          if ((VF.File()->Flags & pkgCache::Flag::NotSource) == pkgCache::Flag::NotSource &&
147              Pkg.CurrentVer() != Ver)
148             continue;
149                          
150          signed Prio = PFPriority[VF.File()->ID];
151          if (Prio > Max)
152          {
153             Pref = Ver;
154             Max = Prio;
155          }       
156       }      
157       
158       if (Pkg.CurrentVer() == Ver && Max < 1000)
159       {
160          /* Elevate our current selection (or the status file itself)
161             to the Pseudo-status priority. */
162          if (Pref.end() == true)
163             Pref = Ver;
164          Max = 1000;
165          
166          // Fast path optimize.
167          if (StatusOverride == false)
168             break;
169       }            
170    }
171    return Pref;
172 }
173                                                                         /*}}}*/
174 // Policy::CreatePin - Create an entry in the pin table..               /*{{{*/
175 // ---------------------------------------------------------------------
176 /* For performance we have 3 tables, the default table, the main cache
177    table (hashed to the cache). A blank package name indicates the pin
178    belongs to the default table. Order of insertion matters here, the
179    earlier defaults override later ones. */
180 void pkgPolicy::CreatePin(pkgVersionMatch::MatchType Type,string Name,
181                           string Data,signed short Priority)
182 {
183    Pin *P = 0;
184    
185    if (Name.empty() == true)
186       P = &*Defaults.insert(Defaults.end(),PkgPin());
187    else
188    {
189       // Get a spot to put the pin
190       pkgCache::PkgIterator Pkg = Cache->FindPkg(Name);
191       if (Pkg.end() == true)
192       {
193          // Check the unmatched table
194          for (vector<PkgPin>::iterator I = Unmatched.begin(); 
195               I != Unmatched.end() && P == 0; I++)
196             if (I->Pkg == Name)
197                P = &*I;
198          
199          if (P == 0)
200             P = &*Unmatched.insert(Unmatched.end(),PkgPin());      
201       }
202       else
203       {
204          P = Pins + Pkg->ID;
205       }      
206    }
207    
208    // Set..
209    P->Type = Type;
210    P->Priority = Priority;
211    P->Data = Data;
212 }
213                                                                         /*}}}*/
214 // Policy::GetMatch - Get the matching version for a package pin        /*{{{*/
215 // ---------------------------------------------------------------------
216 /* */
217 pkgCache::VerIterator pkgPolicy::GetMatch(pkgCache::PkgIterator Pkg)
218 {
219    const Pin &PPkg = Pins[Pkg->ID];
220    if (PPkg.Type != pkgVersionMatch::None)
221    {
222       pkgVersionMatch Match(PPkg.Data,PPkg.Type);
223       return Match.Find(Pkg);
224    }
225    return pkgCache::VerIterator(*Pkg.Cache());
226 }
227                                                                         /*}}}*/
228 // Policy::GetPriority - Get the priority of the package pin            /*{{{*/
229 // ---------------------------------------------------------------------
230 /* */
231 signed short pkgPolicy::GetPriority(pkgCache::PkgIterator const &Pkg)
232 {
233    if (Pins[Pkg->ID].Type != pkgVersionMatch::None)
234    {
235       // In this case 0 means default priority
236       if (Pins[Pkg->ID].Priority == 0)
237          return 989;
238       return Pins[Pkg->ID].Priority;
239    }
240    
241    return 0;
242 }
243                                                                         /*}}}*/
244 // CNC:2003-03-06
245 // Policy::GetPkgPriority - Return a package priority                   /*{{{*/
246 // ---------------------------------------------------------------------
247 /* Evaluate the package pins and the default list to deteremine what the
248    best package is. This is a hacked version of GetCandidateVer(). */
249 signed short pkgPolicy::GetPkgPriority(const pkgCache::PkgIterator &Pkg)
250 {
251    // Look for a package pin and evaluate it.
252    signed Max = GetPriority(Pkg);
253
254    /* Falling through to the default version.. Setting Max to zero
255       effectively excludes everything <= 0 which are the non-automatic
256       priorities.. The status file is given a prio of 100 which will exclude
257       not-automatic sources, except in a single shot not-installed mode.
258       The second pseduo-status file is at prio 1000, above which will permit
259       the user to force-downgrade things.
260       
261       The user pin is subject to the same priority rules as default 
262       selections. Thus there are two ways to create a pin - a pin that
263       tracks the default when the default is taken away, and a permanent
264       pin that stays at that setting.
265     */
266    for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; Ver++)
267    {
268       for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; VF++)
269       {
270          /* If this is the status file, and the current version is not the
271             version in the status file (ie it is not installed, or somesuch)
272             then it is not a candidate for installation, ever. This weeds
273             out bogus entries that may be due to config-file states, or
274             other. */
275          if ((VF.File()->Flags & pkgCache::Flag::NotSource) == pkgCache::Flag::NotSource &&
276              Pkg.CurrentVer() != Ver)
277             continue;
278                          
279          signed Prio = PFPriority[VF.File()->ID];
280          if (Prio > Max)
281             Max = Prio;
282       }      
283    }
284    return Max;
285 }
286                                                                         /*}}}*/
287
288 // ReadPinFile - Load the pin file into a Policy                        /*{{{*/
289 // ---------------------------------------------------------------------
290 /* I'd like to see the preferences file store more than just pin information
291    but right now that is the only stuff I have to store. Later there will
292    have to be some kind of combined super parser to get the data into all
293    the right classes.. */
294 bool ReadPinFile(pkgPolicy &Plcy,string File)
295 {
296    if (File.empty() == true)
297       File = _config->FindFile("Dir::Etc::Preferences");
298
299    if (FileExists(File) == false)
300       return true;
301    
302    FileFd Fd(File,FileFd::ReadOnly);
303    pkgTagFile TF(&Fd);
304    if (_error->PendingError() == true)
305       return false;
306    
307    pkgTagSection Tags;
308    while (TF.Step(Tags) == true)
309    {
310       string Name = Tags.FindS("Package");
311       if (Name.empty() == true)
312          return _error->Error(_("Invalid record in the preferences file, no Package header"));
313       if (Name == "*")
314          Name = string();
315       
316       const char *Start;
317       const char *End;
318       if (Tags.Find("Pin",Start,End) == false)
319          continue;
320          
321       const char *Word = Start;
322       for (; Word != End && isspace(*Word) == 0; Word++);
323
324       // Parse the type..
325       pkgVersionMatch::MatchType Type;
326       if (stringcasecmp(Start,Word,"version") == 0 && Name.empty() == false)
327          Type = pkgVersionMatch::Version;
328       else if (stringcasecmp(Start,Word,"release") == 0)
329          Type = pkgVersionMatch::Release;
330       else if (stringcasecmp(Start,Word,"origin") == 0)
331          Type = pkgVersionMatch::Origin;
332       else
333       {
334          _error->Warning(_("Did not understand pin type %s"),string(Start,Word).c_str());
335          continue;
336       }
337       for (; Word != End && isspace(*Word) != 0; Word++);
338
339       short int priority = Tags.FindI("Pin-Priority", 0);
340       if (priority == 0)
341       {
342          _error->Warning(_("No priority (or zero) specified for pin"));
343          continue;
344       }
345
346       Plcy.CreatePin(Type,Name,string(Word,End),priority);
347    }
348
349    Plcy.InitDefaults();
350    return true;
351 }
352                                                                         /*}}}*/