generalize matching code from examples/solv.c to src/selection.c
[platform/upstream/libsolv.git] / src / evr.c
1 /*
2  * Copyright (c) 2007-2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * evr.c
10  *
11  * version compare
12  */
13
14 #include <stdio.h>
15 #include <string.h>
16 #include "evr.h"
17 #include "pool.h"
18
19
20
21 #if defined(DEBIAN) || defined(MULTI_SEMANTICS)
22
23 /* debian type version compare */
24 int
25 solv_vercmp_deb(const char *s1, const char *q1, const char *s2, const char *q2)
26 {
27   int r, c1, c2;
28   while (1)
29     {
30       c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
31       c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
32       if ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
33         {
34           while (c1 == '0')
35             c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
36           while (c2 == '0')
37             c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
38           r = 0;
39           while ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
40             {
41               if (!r)
42                 r = c1 - c2;
43               c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
44               c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
45             }
46           if (c1 >= '0' && c1 <= '9')
47             return 1;
48           if (c2 >= '0' && c2 <= '9')
49             return -1;
50           if (r)
51             return r < 0 ? -1 : 1;
52         }
53       c1 = c1 == '~' ? -1 : !c1 || (c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'Z') || (c1 >= 'a' && c1 <= 'z')  ? c1 : c1 + 256;
54       c2 = c2 == '~' ? -1 : !c2 || (c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'Z') || (c2 >= 'a' && c2 <= 'z')  ? c2 : c2 + 256;
55       r = c1 - c2;
56       if (r)
57         return r < 0 ? -1 : 1;
58       if (!c1)
59         return 0;
60     }
61 }
62
63 #endif
64
65 #if !defined(DEBIAN) || defined(MULTI_SEMANTICS)
66
67 /* rpm type version compare */
68 /* note: the code assumes that *q1 and *q2 are not alphanumeric! */
69
70 int
71 solv_vercmp_rpm(const char *s1, const char *q1, const char *s2, const char *q2)
72 {
73   int r = 0;
74   const char *e1, *e2;
75
76   for (;;)
77     {
78       while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
79           !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z') && *s1 != '~')
80         s1++;
81       while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
82           !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z') && *s2 != '~')
83         s2++;
84       if (s1 < q1 && *s1 == '~')
85         {
86           if (s2 < q2 && *s2 == '~')
87             {
88               s1++;
89               s2++;
90               continue;
91             }
92           return -1;
93         }
94       if (s2 < q2 && *s2 == '~')
95         return 1;
96       if (s1 >= q1 || s2 >= q2)
97         break;
98       if ((*s1 >= '0' && *s1 <= '9') || (*s2 >= '0' && *s2 <= '9'))
99         {
100           while (*s1 == '0' && s1[1] >= '0' && s1[1] <= '9')
101             s1++;
102           while (*s2 == '0' && s2[1] >= '0' && s2[1] <= '9')
103             s2++;
104           for (e1 = s1; *e1 >= '0' && *e1 <= '9'; )
105             e1++;
106           for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
107             e2++;
108           r = (e1 - s1) - (e2 - s2);
109           if (!r)
110             r = strncmp(s1, s2, e1 - s1);
111           if (r)
112             return r > 0 ? 1 : -1;
113         }
114       else
115         {
116           for (e1 = s1; (*e1 >= 'a' && *e1 <= 'z') || (*e1 >= 'A' && *e1 <= 'Z'); )
117             e1++;
118           for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
119             e2++;
120           r = (e1 - s1) - (e2 - s2);
121           if (r > 0)
122             {
123               r = strncmp(s1, s2, e2 - s2);
124               return r >= 0 ? 1 : -1;
125             }
126           if (r < 0)
127             {
128               r = strncmp(s1, s2, e1 - s1);
129               return r <= 0 ? -1 : 1;
130             }
131           r = strncmp(s1, s2, e1 - s1);
132           if (r)
133             return r > 0 ? 1 : -1;
134         }
135       s1 = e1;
136       s2 = e2;
137     }
138   return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
139 }
140
141 int
142 solv_vercmp_rpm_notilde(const char *s1, const char *q1, const char *s2, const char *q2)
143 {
144   int r = 0;
145   const char *e1, *e2;
146
147   while (s1 < q1 && s2 < q2)
148     {
149       while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
150           !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z'))
151         s1++;
152       while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
153           !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z'))
154         s2++;
155       if ((*s1 >= '0' && *s1 <= '9') || (*s2 >= '0' && *s2 <= '9'))
156         {
157           while (*s1 == '0' && s1[1] >= '0' && s1[1] <= '9')
158             s1++;
159           while (*s2 == '0' && s2[1] >= '0' && s2[1] <= '9')
160             s2++;
161           for (e1 = s1; *e1 >= '0' && *e1 <= '9'; )
162             e1++;
163           for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
164             e2++;
165           r = (e1 - s1) - (e2 - s2);
166           if (!r)
167             r = strncmp(s1, s2, e1 - s1);
168           if (r)
169             return r > 0 ? 1 : -1;
170         }
171       else
172         {
173           for (e1 = s1; (*e1 >= 'a' && *e1 <= 'z') || (*e1 >= 'A' && *e1 <= 'Z'); )
174             e1++;
175           for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
176             e2++;
177           r = (e1 - s1) - (e2 - s2);
178           if (r > 0)
179             {
180               r = strncmp(s1, s2, e2 - s2);
181               return r >= 0 ? 1 : -1;
182             }
183           if (r < 0)
184             {
185               r = strncmp(s1, s2, e1 - s1);
186               return r <= 0 ? -1 : 1;
187             }
188           r = strncmp(s1, s2, e1 - s1);
189           if (r)
190             return r > 0 ? 1 : -1;
191         }
192       s1 = e1;
193       s2 = e2;
194     }
195   return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
196 }
197
198 #endif
199
200
201 /* 
202  * the solv_vercmp variant your system uses.
203  */
204 int
205 solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
206 {
207 #if defined(DEBIAN)
208   return solv_vercmp_deb(s1, q1, s2, q2);
209 #elif defined(ARCHLINUX)
210   return solv_vercmp_rpm_notilde(s1, q1, s2, q2);
211 #else
212   return solv_vercmp_rpm(s1, q1, s2, q2);
213 #endif
214 }
215
216 #if defined(MULTI_SEMANTICS)
217 # define solv_vercmp (*(pool->disttype == DISTTYPE_DEB ? &solv_vercmp_deb : &solv_ver##cmp_rpm))
218 #elif defined(DEBIAN)
219 # define solv_vercmp solv_vercmp_deb
220 #elif defined(ARCHLINUX)
221 # define solv_vercmp solv_vercmp_rpm_notilde
222 #else
223 # define solv_vercmp solv_vercmp_rpm
224 #endif
225
226 /* edition (e:v-r) compare */
227 int
228 pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode)
229 {
230   int r;
231   const char *s1, *s2;
232   const char *r1, *r2;
233
234   if (evr1 == evr2)
235     return 0;
236
237 #if 0
238   POOL_DEBUG(DEBUG_EVRCMP, "evrcmp %s %s mode=%d\n", evr1, evr2, mode);
239 #endif
240   for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
241     ;
242   for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++)
243     ;
244   if (mode == EVRCMP_MATCH && (*evr1 == ':' || *evr2 == ':'))
245     {
246       /* empty epoch, skip epoch check */
247       if (*s1 == ':')
248         evr1 = s1 + 1;
249       if (*s2 == ':')
250         evr2 = s2 + 1;
251       s1 = evr1;
252       s2 = evr2;
253     }
254
255   /* compare the epoch */
256   if (s1 == evr1 || *s1 != ':')
257     s1 = 0;
258   if (s2 == evr2 || *s2 != ':')
259     s2 = 0;
260   if (s1 && s2)
261     {
262       r = solv_vercmp(evr1, s1, evr2, s2);
263       if (r)
264         return r;
265       evr1 = s1 + 1;
266       evr2 = s2 + 1;
267     }
268   else if (s1)
269     {
270       if (!pool->promoteepoch)
271         {
272           while (*evr1 == '0')
273             evr1++;
274           if (*evr1 != ':')
275             return 1;
276         }
277       evr1 = s1 + 1;
278     }
279   else if (s2)
280     {
281       while (*evr2 == '0')
282         evr2++;
283       if (*evr2 != ':')
284         return -1;
285       evr2 = s2 + 1;
286     }
287
288   /* same epoch, now split into version/release */
289   for (s1 = evr1, r1 = 0; *s1; s1++)
290     if (*s1 == '-')
291       r1 = s1;
292   for (s2 = evr2, r2 = 0; *s2; s2++)
293     if (*s2 == '-')
294       r2 = s2;
295
296   r = 0;
297   if (mode != EVRCMP_MATCH || (evr1 != (r1 ? r1 : s1) && evr2 != (r2 ? r2 : s2)))
298     r = solv_vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
299   if (r)
300     return r;
301
302   if (mode == EVRCMP_COMPARE)
303     {
304       if (!r1 && r2)
305         return -1;
306       if (r1 && !r2)
307         return 1;
308     }
309   if (mode == EVRCMP_COMPARE_EVONLY)
310     return 0;
311   if (mode == EVRCMP_MATCH_RELEASE)
312     {
313       /* rpm treats empty releases as missing, i.e "foo = 4-" is the same as "foo = 4" */
314       if (r1 && r1 + 1 == s1)
315         r1 = 0;
316       if (r2 && r2 + 1 == s2)
317         r2 = 0;
318     }
319   if (r1 && r2)
320     {
321       r1++;
322       r2++;
323       if (mode != EVRCMP_MATCH || (s1 != r1 && s2 != r2))
324         {
325           if (pool->havedistepoch)
326             {
327               const char *d1, *d2;
328               for (d1 = r1; d1 < s1; d1++)
329                 if (*d1 == ':')
330                   break;
331               for (d2 = r2; d2 < s2; d2++)
332                 if (*d2 == ':')
333                   break;
334               /* XXX: promote just in one direction? */
335               r = solv_vercmp(r1, d1 ? d1 : s1, r2, d2 ? d2 : s2);
336               if (r == 0 && d1 < s1 && d2 < s2)
337                 r = solv_vercmp(d1 + 1, s1, d2 + 1, s2);
338             }
339           else
340             r = solv_vercmp(r1, s1, r2, s2);
341         }
342     }
343   else if (mode == EVRCMP_MATCH_RELEASE)
344     {
345       if (!r1 && r2)
346         return -2;
347       if (r1 && !r2)
348         return 2;
349     }
350   return r;
351 }
352
353 int
354 pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode)
355 {
356   const char *evr1, *evr2;
357   if (evr1id == evr2id)
358     return 0;
359   evr1 = pool_id2str(pool, evr1id);
360   evr2 = pool_id2str(pool, evr2id);
361   return pool_evrcmp_str(pool, evr1, evr2, mode);
362 }
363
364 int
365 pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release)
366 {
367   const char *evr1;
368   const char *s1;
369   const char *r1;
370   int r;
371
372   evr1 = pool_id2str(pool, evrid);
373   for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
374     ;
375   if (s1 != evr1 && *s1 == ':')
376     {
377       if (epoch)
378         {
379           r = solv_vercmp(evr1, s1, epoch, epoch + strlen(epoch));
380           if (r)
381             return r;
382         }
383       evr1 = s1 + 1;
384     }
385   else if (epoch)
386     {
387       while (*epoch == '0')
388         epoch++;
389       if (*epoch)
390         return -1;
391     }
392   for (s1 = evr1, r1 = 0; *s1; s1++)
393     if (*s1 == '-')
394       r1 = s1;
395   if (version)
396     {
397       r = solv_vercmp(evr1, r1 ? r1 : s1, version, version + strlen(version));
398       if (r)
399         return r;
400     }
401   if (release)
402     {
403       if (!r1)
404         return -1;
405       r = solv_vercmp(r1 + 1, s1, release, release + strlen(release));
406       if (r)
407         return r;
408     }
409   return 0;
410 }
411