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