- initial import of revision 374 from cnc
[apt.git] / apt-pkg / versionmatch.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: versionmatch.cc,v 1.9 2003/05/19 17:58:26 doogie Exp $
4 /* ######################################################################
5
6    Version Matching 
7    
8    This module takes a matching string and a type and locates the version
9    record that satisfies the constraint described by the matching string.
10    
11    ##################################################################### */
12                                                                         /*}}}*/
13 // Include Files                                                        /*{{{*/
14 #ifdef __GNUG__
15 #pragma implementation "apt-pkg/versionmatch.h"
16 #endif
17 #include <apt-pkg/versionmatch.h>
18 // CNC:2003-11-05
19 #include <apt-pkg/version.h>
20
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/error.h>
23
24 #include <stdio.h>
25 #include <ctype.h>
26                                                                         /*}}}*/
27
28 // VersionMatch::pkgVersionMatch - Constructor                          /*{{{*/
29 // ---------------------------------------------------------------------
30 /* Break up the data string according to the selected type */
31 // CNC:2003-11-05
32 pkgVersionMatch::pkgVersionMatch(string Data,MatchType Type,int Op) : VerOp(Op), Type(Type)
33 {
34    MatchAll = false;
35    VerPrefixMatch = false;
36    RelVerPrefixMatch = false;
37    
38    if (Type == None || Data.length() < 1)
39       return;
40    
41    // Cut up the version representation
42    if (Type == Version)
43    {
44       if (Data.end()[-1] == '*')
45       {
46          VerPrefixMatch = true;
47          VerStr = string(Data,0,Data.length()-1);
48       }
49       else
50          VerStr = Data;
51       return;
52    }   
53    
54    if (Type == Release)
55    {
56       // All empty = match all
57       if (Data == "*")
58       {
59          MatchAll = true;
60          return;
61       }
62       
63       // Are we a simple specification?
64       string::const_iterator I = Data.begin();
65       for (; I != Data.end() && *I != '='; I++);
66       if (I == Data.end())
67       {
68          // Temporary
69          if (isdigit(Data[0]))
70             RelVerStr = Data;
71          else
72             RelArchive = Data;
73          
74          if (RelVerStr.length() > 0 && RelVerStr.end()[-1] == '*')
75          {
76             RelVerPrefixMatch = true;
77             RelVerStr = string(RelVerStr.begin(),RelVerStr.end()-1);
78          }       
79          return;
80       }
81             
82       char Spec[300];
83       char *Fragments[20];
84       snprintf(Spec,sizeof(Spec),"%s",Data.c_str());
85       if (TokSplitString(',',Spec,Fragments,
86                          sizeof(Fragments)/sizeof(Fragments[0])) == false)
87       {
88          Type = None;
89          return;
90       }
91       
92       for (unsigned J = 0; Fragments[J] != 0; J++)
93       {
94          if (strlen(Fragments[J]) < 3)
95             continue;
96             
97          if (stringcasecmp(Fragments[J],Fragments[J]+2,"v=") == 0)
98             RelVerStr = Fragments[J]+2;
99          else if (stringcasecmp(Fragments[J],Fragments[J]+2,"o=") == 0)
100             RelOrigin = Fragments[J]+2;
101          else if (stringcasecmp(Fragments[J],Fragments[J]+2,"a=") == 0)
102             RelArchive = Fragments[J]+2;
103          else if (stringcasecmp(Fragments[J],Fragments[J]+2,"l=") == 0)
104             RelLabel = Fragments[J]+2;
105          else if (stringcasecmp(Fragments[J],Fragments[J]+2,"c=") == 0)
106             RelComponent = Fragments[J]+2;
107       }
108       
109       if (RelVerStr.end()[-1] == '*')
110       {
111          RelVerPrefixMatch = true;
112          RelVerStr = string(RelVerStr.begin(),RelVerStr.end()-1);
113       }  
114       return;
115    }
116    
117    if (Type == Origin)
118    {
119       OrSite = Data;
120       return;
121    }   
122 }
123                                                                         /*}}}*/
124 // VersionMatch::MatchVer - Match a version string with prefixing       /*{{{*/
125 // ---------------------------------------------------------------------
126 /* */
127 bool pkgVersionMatch::MatchVer(const char *A,string B,bool Prefix)
128 {   
129    // CNC:2003-11-05 - Patch by ALT-Linux, which ignores the release
130    //                  if it was not provided, and the epoch.
131    string s(A), sc(A);
132    const char *Ab = s.c_str(), *Ac = sc.c_str();
133
134    for (string::iterator i = s.begin(), k = sc.begin(); i != s.end(); ++i,++k)
135    {
136       if (*i == ':')
137       {
138          Ab = &(*i) + 1;
139          Ac = &(*k) + 1;
140       }
141       else if (*i == '-')
142       {
143          *i = 0;
144          break;
145       }
146    }
147
148    const char *Ae = Ab + strlen(Ab);
149    const char *Af = Ac + strlen(Ac);
150    
151    // Strings are not a compatible size.
152    if (((unsigned)(Ae - Ab) == B.length() || Prefix == true) &&
153        (unsigned)(Ae - Ab) >= B.length() &&
154        stringcasecmp(B,Ab,Ab + B.length()) == 0)
155        return true;
156    else if (((unsigned)(Af - Ac) == B.length() || Prefix == true) &&
157        (unsigned)(Af - Ac) >= B.length() &&
158        stringcasecmp(B,Ac,Ac + B.length()) == 0)
159        return true;
160
161    return false;
162 }
163                                                                         /*}}}*/
164 // VersionMatch::Find - Locate the best match for the select type       /*{{{*/
165 // ---------------------------------------------------------------------
166 /* */
167 pkgCache::VerIterator pkgVersionMatch::Find(pkgCache::PkgIterator Pkg)
168 {
169    // CNC:2003-11-05
170    pkgVersioningSystem *VS = Pkg.Cache()->VS;
171    pkgCache::VerIterator Ver = Pkg.VersionList();
172
173    for (; Ver.end() == false; Ver++)
174    {
175       if (Type == Version)
176       {
177          // CNC:2003-11-05
178          if (VerPrefixMatch)
179          {
180             if (MatchVer(Ver.VerStr(),VerStr,VerPrefixMatch) == true)
181                return Ver;
182          } else {
183             if (VS->CheckDep(Ver.VerStr(),VerOp,VerStr.c_str()) == true)
184                return Ver;
185          }
186
187          continue;
188       }
189       
190       for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; VF++)
191          if (FileMatch(VF.File()) == true)
192             return Ver;
193    }
194       
195    // CNC:2003-11-11 - Virtual package handling.
196    if (Type == Version)
197    {
198       bool HasRelease = (strchr(VerStr.c_str(), '-') != NULL);
199       pkgCache::PrvIterator Prv = Pkg.ProvidesList();
200       for (; Prv.end() == false; Prv++)
201       {
202          const char *PrvVerStr = Prv.ProvideVersion();
203          if (PrvVerStr == NULL || PrvVerStr[0] == 0)
204             continue;
205          if (VerPrefixMatch || (HasRelease && strchr(PrvVerStr, '-') == NULL))
206          {
207             if (MatchVer(PrvVerStr,VerStr,VerPrefixMatch) == true)
208                return Prv.OwnerVer();
209          } else {
210             if (VS->CheckDep(PrvVerStr,VerOp,VerStr.c_str()) == true)
211                return Prv.OwnerVer();
212          }
213       }
214    }
215
216    // This will be Ended by now.
217    return Ver;
218 }
219                                                                         /*}}}*/
220 // VersionMatch::FileMatch - Match against an index file                /*{{{*/
221 // ---------------------------------------------------------------------
222 /* This matcher checks against the release file and the origin location 
223    to see if the constraints are met. */
224 bool pkgVersionMatch::FileMatch(pkgCache::PkgFileIterator File)
225 {
226    if (Type == Release)
227    {
228       if (MatchAll == true)
229          return true;
230       
231 /*      cout << RelVerStr << ',' << RelOrigin << ',' << RelArchive << ',' << RelLabel << endl;
232       cout << File.Version() << ',' << File.Origin() << ',' << File.Archive() << ',' << File.Label() << endl;*/
233       
234       if (RelVerStr.empty() == true && RelOrigin.empty() == true &&
235           RelArchive.empty() == true && RelLabel.empty() == true &&
236           RelComponent.empty() == true)
237          return false;
238       
239       if (RelVerStr.empty() == false)
240          if (File->Version == 0 ||
241              MatchVer(File.Version(),RelVerStr,RelVerPrefixMatch) == false)
242             return false;
243       if (RelOrigin.empty() == false)
244          if (File->Origin == 0 ||
245              stringcasecmp(RelOrigin,File.Origin()) != 0)
246             return false;
247       if (RelArchive.empty() == false)
248       {
249          if (File->Archive == 0 || 
250              stringcasecmp(RelArchive,File.Archive()) != 0)
251             return false;
252       }      
253       if (RelLabel.empty() == false)
254          if (File->Label == 0 ||
255              stringcasecmp(RelLabel,File.Label()) != 0)
256             return false;
257       if (RelComponent.empty() == false)
258          if (File->Component == 0 ||
259              stringcasecmp(RelComponent,File.Component()) != 0)
260             return false;
261       return true;
262    }
263    
264    if (Type == Origin)
265    {
266       if (OrSite.empty() == false) {
267          if (File->Site == 0 || OrSite != File.Site())
268             return false;
269       } else // so we are talking about file:// or status file
270          if (strcmp(File.Site(),"") == 0 && File->Archive != 0) // skip the status file
271             return false;
272       return (OrSite == File.Site());           /* both strings match */
273    }
274    
275    return false;
276 }
277                                                                         /*}}}*/
278
279 // vim:sts=3:sw=3