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