generalize matching code from examples/solv.c to src/selection.c
[platform/upstream/libsolv.git] / src / selection.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * selection.c
10  *
11  */
12
13 #define _GNU_SOURCE
14 #include <string.h>
15 #include <fnmatch.h>
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 #include "selection.h"
22 #include "solver.h"
23
24
25 static int
26 str2archid(Pool *pool, const char *arch)
27 {
28   Id id;
29   if (!*arch)
30     return 0;
31   id = pool_str2id(pool, arch, 0);
32   if (!id || id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
33     return id;
34   if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
35     return 0;
36   return id;
37 }
38
39 static void
40 selection_prune(Pool *pool, Queue *selection)
41 {
42   int i, j;
43   Id p, pp;
44   for (i = j = 0; i < selection->count; i += 2)
45     {
46       Id select = selection->elements[i] & SOLVER_SELECTMASK;
47       p = 0;
48       if (select == SOLVER_SOLVABLE_ALL)
49         p = 1;
50       else if (select == SOLVER_SOLVABLE_REPO)
51         {
52           Solvable *s;
53           Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
54           if (repo)
55             FOR_REPO_SOLVABLES(repo, p, s)
56               break;
57         }
58       else
59         {
60           FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
61             break;
62         }
63       if (!p)
64         continue;
65       selection->elements[j] = selection->elements[i];
66       selection->elements[j + 1] = selection->elements[i + 1];
67       j += 2;
68     }
69   queue_truncate(selection, j);
70 }
71
72
73 static int
74 selection_flatten_sortcmp(const void *ap, const void *bp, void *dp)
75 {
76   return *(const Id *)ap - *(const Id *)bp;
77 }
78
79 static void
80 selection_flatten(Pool *pool, Queue *selection)
81 {
82   Queue q;
83   int i, j;
84   Id p, pp, lastid;
85   if (selection->count <= 1)
86     return;
87   queue_init(&q);
88   for (i = 0; i < selection->count; i += 2)
89     {
90       Id select = selection->elements[i] & SOLVER_SELECTMASK;
91       if (select == SOLVER_SOLVABLE_ALL)
92         {
93           selection->elements[0] = selection->elements[i];
94           selection->elements[1] = selection->elements[i + 1];
95           queue_truncate(selection, 2);
96           return;
97         }
98       if (select == SOLVER_SOLVABLE_REPO)
99         {
100           Solvable *s;
101           Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
102           if (repo)
103             FOR_REPO_SOLVABLES(repo, p, s)
104               queue_push(&q, p);
105         }
106       else
107         {
108           FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
109             queue_push(&q, p);
110         }
111     }
112   if (!q.count)
113     {
114       queue_empty(selection);
115       return;
116     }
117   if (q.count > 1)
118     {
119       /* sort and unify */
120       solv_sort(q.elements, q.count, sizeof(Id), selection_flatten_sortcmp, NULL);
121       lastid = q.elements[0];
122       for (i = j = 1; i < q.count; i++)
123         if (q.elements[i] != lastid)
124           q.elements[j++] = lastid = q.elements[i];
125       queue_truncate(&q, j);
126     }
127   queue_truncate(selection, 2);
128   if (q.count > 1)
129     {
130       selection->elements[0] = SOLVER_SOLVABLE_ONE_OF;
131       selection->elements[1] = pool_queuetowhatprovides(pool, &q);
132     }
133   else
134     {
135       selection->elements[0] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
136       selection->elements[1] = q.elements[0];
137     }
138 }
139
140 static void
141 selection_limit_rel(Pool *pool, Queue *selection, Id flags, Id evr)
142 {
143   int i, j;
144   for (i = j = 0; i < selection->count; i += 2)
145     {
146       Id select = selection->elements[i] & SOLVER_SELECTMASK;
147       if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES)
148         continue;       /* actually internal error */
149       selection->elements[i + 1] = pool_rel2id(pool, selection->elements[i + 1], evr, flags, 1);
150       if (flags == REL_ARCH)
151         selection->elements[i] |= SOLVER_SETARCH;
152       if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && selection->elements[i])
153         {
154           if (pool->disttype == DISTTYPE_DEB)
155             selection->elements[i] |= SOLVER_SETEVR;    /* debian can't match version only like rpm */
156           else
157             selection->elements[i] |= strchr(pool_id2str(pool, evr), '-') != 0 ? SOLVER_SETEVR : SOLVER_SETEV;
158         }
159     }
160   selection_prune(pool, selection);
161 }
162
163 static int
164 selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
165 {
166   Id id, p, pp;
167   int i, match = 0;
168   int doglob = 0;
169   int globflags = 0;
170
171   if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES)))
172     return 0;
173
174   if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
175     return 0;
176
177   if (!(flags & SELECTION_NOCASE))
178     {
179       id = pool_str2id(pool, name, 0);
180       if (id)
181         {
182           FOR_PROVIDES(p, pp, id)
183             {
184               Solvable *s = pool->solvables + p;
185               if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
186                 continue;
187               match = 1;
188               if (s->name == id && (flags & SELECTION_NAME) != 0)
189                 {
190                   queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
191                   return SELECTION_NAME;
192                 }
193             }
194           if (match && (flags & SELECTION_PROVIDES) != 0)
195             {
196               queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
197               return SELECTION_PROVIDES;
198             }
199         }
200     }
201
202   if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0)
203     doglob = 1;
204
205   if (!doglob && !(flags & SELECTION_NOCASE))
206     return 0;
207
208   if (doglob && (flags & SELECTION_NOCASE) != 0)
209     globflags = FNM_CASEFOLD;
210
211 #if 0   /* doesn't work with selection_limit_rel yet */
212   if (doglob && !strcmp(name, "*") && (flags & SELECTION_FLAT) != 0)
213     {
214       /* can't do this for SELECTION_PROVIDES, as src rpms don't provide anything */
215       if ((flags & SELECTION_NAME) != 0)
216         {
217           queue_push2(selection, SOLVER_SOLVABLE_ALL, 0);
218           return SELECTION_NAME;
219         }
220     }
221 #endif
222
223   if ((flags & SELECTION_NAME) != 0)
224     {
225       /* looks like a name glob. hard work. */
226       for (p = 1; p < pool->nsolvables; p++)
227         {
228           Solvable *s = pool->solvables + p;
229           if (!s->repo || !pool_installable(pool, s))
230             continue;
231           if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
232             continue;
233           id = s->name;
234           if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0)
235             {
236               for (i = 0; i < selection->count; i += 2)
237                 if (selection->elements[i] == SOLVER_SOLVABLE_NAME && selection->elements[i + 1] == id)
238                   break;
239               if (i == selection->count)
240                 queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
241               match = 1;
242             }
243         }
244       if (match)
245         return SELECTION_NAME;
246     }
247   if ((flags & SELECTION_PROVIDES))
248     {
249       /* looks like a dep glob. really hard work. */
250       for (id = 1; id < pool->ss.nstrings; id++)
251         {
252           if (!pool->whatprovides[id])
253             continue;
254           if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0)
255             {
256               if ((flags & SELECTION_INSTALLED_ONLY) != 0)
257                 {
258                   FOR_PROVIDES(p, pp, id)
259                     if (pool->solvables[p].repo == pool->installed)
260                       break;
261                   if (!p)
262                     continue;
263                 }
264               queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
265               match = 1;
266             }
267         }
268       if (match)
269         return SELECTION_PROVIDES;
270     }
271   return 0;
272 }
273
274 static int
275 selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags)
276 {
277   int ret;
278   const char *r;
279   Id archid;
280
281   if ((ret = selection_depglob(pool, selection, name, flags)) != 0)
282     return ret;
283   /* check if theres an .arch suffix */
284   if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
285     {
286       char *rname = solv_strdup(name);
287       rname[r - name] = 0;
288       if ((ret = selection_depglob(pool, selection, rname, flags)) != 0)
289         {
290           selection_limit_rel(pool, selection, REL_ARCH, archid);
291           solv_free(rname);
292           return ret;
293         }
294       solv_free(rname);
295     }
296   return 0;
297 }
298
299 static int
300 selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
301 {
302   Dataiterator di;
303   Queue q;
304   int type;
305
306   type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
307   if ((flags & SELECTION_NOCASE) != 0)
308     type |= SEARCH_NOCASE;
309   queue_init(&q);
310   dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
311   while (dataiterator_step(&di))
312     {
313       Solvable *s = pool->solvables + di.solvid;
314       if (!s->repo || !pool_installable(pool, s))
315         continue;
316       queue_push(&q, di.solvid);
317       dataiterator_skip_solvable(&di);
318     }
319   dataiterator_free(&di);
320   if (!q.count)
321     return 0;
322   if (q.count > 1) 
323     queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
324   else
325     queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
326   queue_free(&q);
327   return SELECTION_FILELIST;
328 }
329
330 static int
331 selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
332 {
333   int ret, rflags = 0;
334   char *r, *rname;
335   
336   /* relation case, support:
337    * depglob rel
338    * depglob.arch rel
339    */
340   rname = solv_strdup(name);
341   if ((r = strpbrk(rname, "<=>")) != 0)
342     {
343       int nend = r - rname;
344       for (; *r; r++)
345         {
346           if (*r == '<')
347             rflags |= REL_LT;
348           else if (*r == '=')
349             rflags |= REL_EQ;
350           else if (*r == '>')
351             rflags |= REL_GT;
352           else
353             break;
354         }
355       while (*r && *r == ' ' && *r == '\t')
356         r++;
357       while (nend && (rname[nend - 1] == ' ' || rname[nend -1 ] == '\t'))
358         nend--;
359       if (!*rname || !*r)
360         {
361           solv_free(rname);
362           return 0;
363         }
364       rname[nend] = 0;
365     }
366   if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0)
367     {
368       if (rflags)
369         selection_limit_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
370       solv_free(rname);
371       return ret;
372     }
373   solv_free(rname);
374   return 0;
375 }
376
377 static int
378 selection_nevra(Pool *pool, Queue *selection, const char *name, int flags)
379 {
380   char *rname, *r, *r2;
381   Id archid = 0;
382   int ret;
383
384   /*
385    * nameglob-version
386    * nameglob-version.arch
387    * nameglob-version-release
388    * nameglob-version-release.arch
389    */
390   flags |= SELECTION_NAME;
391   flags &= ~SELECTION_PROVIDES;
392
393   if (pool->disttype == DISTTYPE_DEB)
394     {
395       if ((r = strchr(name, '_')) == 0)
396         return 0;
397       rname = solv_strdup(name);        /* so we can modify it */
398       r = rname + (r - name);
399       *r++ = 0;
400       if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
401         {
402           solv_free(rname);
403           return 0;
404         }
405       /* is there a vaild arch? */
406       if ((r2 = strchr(r, '_')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
407         {
408           *r2 = 0;      /* split off */
409           selection_limit_rel(pool, selection, REL_ARCH, archid);
410         }
411       selection_limit_rel(pool, selection, flags, pool_str2id(pool, r, 1));
412       solv_free(rname);
413       return ret;
414     }
415
416   if ((r = strrchr(name, '-')) == 0)
417     return 0;
418   rname = solv_strdup(name);    /* so we can modify it */
419   r = rname + (r - name);
420   *r = 0; 
421   /* try with just the version */
422   if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
423     {
424       /* no luck, try with version-release */
425       if ((r2 = strrchr(rname, '-')) == 0)
426         {
427           solv_free(rname);
428           return 0;
429         }
430       *r = '-'; 
431       *r2 = 0; 
432       r = r2;
433       if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
434         {
435           solv_free(rname);
436           return 0;
437         }
438     }
439   /* we now know the name, check if we need to split of the arch */
440   r++;
441   if ((r2 = strrchr(r, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
442     {
443       /* found valid arch, split it off */
444       *r2 = 0;
445       selection_limit_rel(pool, selection, REL_ARCH, archid);
446     }
447   selection_limit_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
448   solv_free(rname);
449   return ret;
450 }
451
452 int
453 selection_make(Pool *pool, Queue *selection, const char *name, int flags)
454 {
455   int ret = 0;
456   const char *r;
457
458   queue_empty(selection);
459   if (*name == '/' && (flags & SELECTION_FILELIST))
460     ret = selection_filelist(pool, selection, name, flags);
461   if (!ret && (r = strpbrk(name, "<=>")) != 0)
462     ret = selection_rel(pool, selection, name, flags);
463   if (!ret)
464     ret = selection_depglob_arch(pool, selection, name, flags);
465   if (!ret && (flags & SELECTION_NAME) != 0)
466     ret = selection_nevra(pool, selection, name, flags);
467   if (ret && (flags & SELECTION_FLAT) != 0)
468     selection_flatten(pool, selection);
469   return ret;
470 }
471
472 void
473 selection_limit(Pool *pool, Queue *sel1, Queue *sel2)
474 {
475   int i, j, miss;
476   Id p, pp;
477   Queue q1;
478   Map m2;
479   Id setflags = 0;
480
481   if (!sel1->count || !sel2->count)
482     {
483       queue_empty(sel1);
484       return;
485     }
486   if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
487     {
488       /* XXX: not 100% correct, but very useful */
489       queue_free(sel1);
490       queue_init_clone(sel1, sel2);
491       return;
492     }
493   queue_init(&q1);
494   map_init(&m2, pool->nsolvables);
495   for (i = 0; i < sel2->count; i += 2)
496     {
497       Id select = sel2->elements[i] & SOLVER_SELECTMASK;
498       if (select == SOLVER_SOLVABLE_ALL)
499         return;
500       if (select == SOLVER_SOLVABLE_REPO)
501         {
502           Solvable *s;
503           Repo *repo = pool_id2repo(pool, sel2->elements[i + 1]);
504           if (repo)
505             FOR_REPO_SOLVABLES(repo, p, s)
506               map_set(&m2, p);
507         }
508       else
509         {
510           FOR_JOB_SELECT(p, pp, select, sel2->elements[i + 1])
511             map_set(&m2, p);
512         }
513     }
514   if (sel2->count == 2)         /* XXX: AND all setmasks instead? */
515     setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
516   for (i = j = 0; i < sel1->count; i += 2)
517     {
518       Id select = sel1->elements[i] & SOLVER_SELECTMASK;
519       queue_empty(&q1);
520       miss = 0;
521       if (select == SOLVER_SOLVABLE_ALL)
522         {
523           for (p = 2; p < pool->nsolvables; p++)
524             if (map_tst(&m2, p))
525               queue_push(&q1, p);
526             else
527               miss = 1;
528         }
529       else if (select == SOLVER_SOLVABLE_REPO)
530         {
531           Solvable *s;
532           Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]);
533           if (repo)
534             FOR_REPO_SOLVABLES(repo, p, s)
535               {
536                 if (map_tst(&m2, p))
537                   queue_push(&q1, p);
538                 else
539                   miss = 1;
540               }
541         }
542       else
543         {
544           FOR_JOB_SELECT(p, pp, select, sel1->elements[i + 1])
545             {
546               if (map_tst(&m2, p))
547                 queue_pushunique(&q1, p);
548               else
549                 miss = 1;
550             }
551         }
552       if (!q1.count)
553         continue;
554       if (!miss)
555         {
556           sel1->elements[j] = sel1->elements[i] | setflags;
557           sel1->elements[j + 1] = sel1->elements[i + 1];
558         }
559       else if (q1.count > 1) 
560         {
561           sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
562           sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1);
563         }
564       else
565         {
566           sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
567           sel1->elements[j + 1] = q1.elements[0];
568         }
569       j += 2;
570     }
571   queue_truncate(sel1, j);
572 }
573
574 void
575 selection_add(Pool *pool, Queue *sel1, Queue *sel2)
576 {
577   int i;
578   for (i = 0; i < sel2->count; i++)
579     queue_push(sel1, sel2->elements[i]);
580 }
581