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