- add pool_error and pool_errstr. get rid of lots of exit() calls.
[platform/upstream/libsolv.git] / src / pool.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  * pool.c
10  * 
11  * The pool contains information about solvables
12  * stored optimized for memory consumption and fast retrieval.
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <unistd.h>
19 #include <string.h>
20
21 #include "pool.h"
22 #include "repo.h"
23 #include "poolid.h"
24 #include "poolid_private.h"
25 #include "poolarch.h"
26 #include "util.h"
27 #include "bitmap.h"
28 #include "evr.h"
29
30 #define SOLVABLE_BLOCK  255
31
32 #define KNOWNID_INITIALIZE
33 #include "knownid.h"
34 #undef KNOWNID_INITIALIZE
35
36 /* create pool */
37 Pool *
38 pool_create(void)
39 {
40   Pool *pool;
41   Solvable *s;
42
43   pool = (Pool *)solv_calloc(1, sizeof(*pool));
44
45   stringpool_init (&pool->ss, initpool_data);
46
47   /* alloc space for RelDep 0 */
48   pool->rels = solv_extend_resize(0, 1, sizeof(Reldep), REL_BLOCK);
49   pool->nrels = 1;
50   memset(pool->rels, 0, sizeof(Reldep));
51
52   /* alloc space for Solvable 0 and system solvable */
53   pool->solvables = solv_extend_resize(0, 2, sizeof(Solvable), SOLVABLE_BLOCK);
54   pool->nsolvables = 2;
55   memset(pool->solvables, 0, 2 * sizeof(Solvable));
56
57   queue_init(&pool->vendormap);
58
59 #if defined(DEBIAN)
60   pool->disttype = DISTTYPE_DEB;
61   pool->noarchid = ARCH_ALL;
62 #elif defined(ARCHLINUX)
63   pool->disttype = DISTTYPE_ARCH;
64   pool->noarchid = ARCH_ANY;
65 #else
66   pool->disttype = DISTTYPE_RPM;
67   pool->noarchid = ARCH_NOARCH;
68 #endif
69
70   /* initialize the system solvable */
71   s = pool->solvables + SYSTEMSOLVABLE;
72   s->name = SYSTEM_SYSTEM;
73   s->arch = pool->noarchid;
74   s->evr = ID_EMPTY;
75
76   pool->debugmask = SOLV_DEBUG_RESULT;  /* FIXME */
77 #ifdef FEDORA
78   pool->obsoleteusescolors = 1;
79 #endif
80 #ifdef RPM5
81   pool->forbidselfconflicts = 1;
82   pool->obsoleteusesprovides = 1;
83   pool->implicitobsoleteusesprovides = 1;
84   pool->havedistepoch = 1;
85 #endif
86   return pool;
87 }
88
89
90 /* free all the resources of our pool */
91 void
92 pool_free(Pool *pool)
93 {
94   int i;
95
96   pool_freewhatprovides(pool);
97   pool_freeidhashes(pool);
98   pool_freeallrepos(pool, 1);
99   solv_free(pool->id2arch);
100   solv_free(pool->id2color);
101   solv_free(pool->solvables);
102   stringpool_free(&pool->ss);
103   solv_free(pool->rels);
104   pool_setvendorclasses(pool, 0);
105   queue_free(&pool->vendormap);
106   for (i = 0; i < POOL_TMPSPACEBUF; i++)
107     solv_free(pool->tmpspace.buf[i]);
108   for (i = 0; i < pool->nlanguages; i++)
109     free((char *)pool->languages[i]);
110   solv_free(pool->languages);
111   solv_free(pool->languagecache);
112   solv_free(pool->errstr);
113   solv_free(pool);
114 }
115
116 void
117 pool_freeallrepos(Pool *pool, int reuseids)
118 {
119   int i;
120
121   pool_freewhatprovides(pool);
122   for (i = 1; i < pool->nrepos; i++) 
123     if (pool->repos[i])
124       repo_freedata(pool->repos[i]);
125   pool->repos = solv_free(pool->repos);
126   pool->nrepos = 0; 
127   pool->urepos = 0; 
128   /* the first two solvables don't belong to a repo */
129   pool_free_solvable_block(pool, 2, pool->nsolvables - 2, reuseids);
130 }
131
132 #ifdef MULTI_SEMANTICS
133 void
134 pool_setdisttype(Pool *pool, int disttype)
135 {
136   pool->disttype = disttype;
137   if (disttype == DISTTYPE_RPM)
138     pool->noarchid = ARCH_NOARCH;
139   if (disttype == DISTTYPE_DEB)
140     pool->noarchid = ARCH_ALL;
141   if (disttype == DISTTYPE_ARCH)
142     pool->noarchid = ARCH_ANY;
143   pool->solvables[SYSTEMSOLVABLE].arch = pool->noarchid;
144 }
145 #endif
146
147 int
148 pool_get_flag(Pool *pool, int flag)
149 {
150   switch (flag)
151     {
152     case POOL_FLAG_PROMOTEEPOCH:
153       return pool->promoteepoch;
154     case POOL_FLAG_FORBIDSELFCONFLICTS:
155       return pool->forbidselfconflicts;
156     case POOL_FLAG_OBSOLETEUSESPROVIDES:
157       return pool->obsoleteusesprovides;
158     case POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES:
159       return pool->implicitobsoleteusesprovides;
160     case POOL_FLAG_OBSOLETEUSESCOLORS:
161       return pool->obsoleteusescolors;
162     case POOL_FLAG_NOINSTALLEDOBSOLETES:
163       return pool->noinstalledobsoletes;
164     case POOL_FLAG_HAVEDISTEPOCH:
165       return pool->havedistepoch;
166     default:
167       break;
168     }
169   return -1;
170 }
171
172 int
173 pool_set_flag(Pool *pool, int flag, int value)
174 {
175   int old = pool_get_flag(pool, flag);
176   switch (flag)
177     {
178     case POOL_FLAG_PROMOTEEPOCH:
179       pool->promoteepoch = value;
180       break;
181     case POOL_FLAG_FORBIDSELFCONFLICTS:
182       pool->forbidselfconflicts = value;
183       break;
184     case POOL_FLAG_OBSOLETEUSESPROVIDES:
185       pool->obsoleteusesprovides = value;
186       break;
187     case POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES:
188       pool->implicitobsoleteusesprovides = value;
189       break;
190     case POOL_FLAG_OBSOLETEUSESCOLORS:
191       pool->obsoleteusescolors = value;
192       break;
193     case POOL_FLAG_NOINSTALLEDOBSOLETES:
194       pool->noinstalledobsoletes = value;
195       break;
196     case POOL_FLAG_HAVEDISTEPOCH:
197       pool->havedistepoch = value;
198       break;
199     default:
200       break;
201     }
202   return old;
203 }
204
205
206 Id
207 pool_add_solvable(Pool *pool)
208 {
209   pool->solvables = solv_extend(pool->solvables, pool->nsolvables, 1, sizeof(Solvable), SOLVABLE_BLOCK);
210   memset(pool->solvables + pool->nsolvables, 0, sizeof(Solvable));
211   return pool->nsolvables++;
212 }
213
214 Id
215 pool_add_solvable_block(Pool *pool, int count)
216 {
217   Id nsolvables = pool->nsolvables;
218   if (!count)
219     return nsolvables;
220   pool->solvables = solv_extend(pool->solvables, pool->nsolvables, count, sizeof(Solvable), SOLVABLE_BLOCK);
221   memset(pool->solvables + nsolvables, 0, sizeof(Solvable) * count);
222   pool->nsolvables += count;
223   return nsolvables;
224 }
225
226 void
227 pool_free_solvable_block(Pool *pool, Id start, int count, int reuseids)
228 {
229   if (!count)
230     return;
231   if (reuseids && start + count == pool->nsolvables)
232     {
233       /* might want to shrink solvable array */
234       pool->nsolvables = start;
235       return;
236     }
237   memset(pool->solvables + start, 0, sizeof(Solvable) * count);
238 }
239
240
241 void
242 pool_set_installed(Pool *pool, Repo *installed)
243 {
244   if (pool->installed == installed)
245     return;
246   pool->installed = installed;
247   pool_freewhatprovides(pool);
248 }
249
250 static int
251 pool_shrink_whatprovides_sortcmp(const void *ap, const void *bp, void *dp)
252 {
253   int r;
254   Pool *pool = dp;
255   Id oa, ob, *da, *db;
256   oa = pool->whatprovides[*(Id *)ap];
257   ob = pool->whatprovides[*(Id *)bp];
258   if (oa == ob)
259     return *(Id *)ap - *(Id *)bp;
260   da = pool->whatprovidesdata + oa;
261   db = pool->whatprovidesdata + ob;
262   while (*db)
263     if ((r = (*da++ - *db++)) != 0)
264       return r;
265   if (*da)
266     return *da;
267   return *(Id *)ap - *(Id *)bp;
268 }
269
270 /*
271  * pool_shrink_whatprovides  - unify whatprovides data
272  *
273  * whatprovides_rel must be empty for this to work!
274  *
275  */
276 static void
277 pool_shrink_whatprovides(Pool *pool)
278 {
279   Id i, n, id;
280   Id *sorted;
281   Id lastid, *last, *dp, *lp;
282   Offset o;
283   int r;
284
285   if (pool->ss.nstrings < 3)
286     return;
287   sorted = solv_malloc2(pool->ss.nstrings, sizeof(Id));
288   for (i = id = 0; id < pool->ss.nstrings; id++)
289     if (pool->whatprovides[id] && pool->whatprovides[id] != 1)
290       sorted[i++] = id;
291   n = i;
292   solv_sort(sorted, n, sizeof(Id), pool_shrink_whatprovides_sortcmp, pool);
293   last = 0;
294   lastid = 0;
295   for (i = 0; i < n; i++)
296     {
297       id = sorted[i];
298       o = pool->whatprovides[id];
299       dp = pool->whatprovidesdata + o;
300       if (last)
301         {
302           lp = last;
303           while (*dp)   
304             if (*dp++ != *lp++)
305               {
306                 last = 0;
307                 break;
308               }
309           if (last && *lp)
310             last = 0;
311           if (last)
312             {
313               pool->whatprovides[id] = -lastid;
314               continue;
315             }
316         }
317       last = pool->whatprovidesdata + o;
318       lastid = id;
319     }
320   solv_free(sorted);
321   dp = pool->whatprovidesdata + 2;
322   for (id = 1; id < pool->ss.nstrings; id++)
323     {
324       o = pool->whatprovides[id];
325       if (o == 0 || o == 1)
326         continue;
327       if ((Id)o < 0)
328         {
329           i = -(Id)o;
330           if (i >= id)
331             abort();
332           pool->whatprovides[id] = pool->whatprovides[i];
333           continue;
334         }
335       lp = pool->whatprovidesdata + o;
336       if (lp < dp)
337         abort();
338       pool->whatprovides[id] = dp - pool->whatprovidesdata;
339       while ((*dp++ = *lp++) != 0)
340         ;
341     }
342   o = dp - pool->whatprovidesdata;
343   POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesdata from %d to %d\n", pool->whatprovidesdataoff, o);
344   if (pool->whatprovidesdataoff == o)
345     return;
346   r = pool->whatprovidesdataoff - o;
347   pool->whatprovidesdataoff = o;
348   pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (o + pool->whatprovidesdataleft) * sizeof(Id));
349   if (r > pool->whatprovidesdataleft)
350     r = pool->whatprovidesdataleft;
351   memset(pool->whatprovidesdata + o, 0, r * sizeof(Id));
352 }
353
354
355 /*
356  * pool_createwhatprovides()
357  * 
358  * create hashes over pool of solvables to ease provide lookups
359  * 
360  */
361 void
362 pool_createwhatprovides(Pool *pool)
363 {
364   int i, num, np, extra;
365   Offset off;
366   Solvable *s;
367   Id id;
368   Offset *idp, n;
369   Offset *whatprovides;
370   Id *whatprovidesdata, *d;
371   Repo *installed = pool->installed;
372   unsigned int now;
373
374   now = solv_timems(0);
375   POOL_DEBUG(SOLV_DEBUG_STATS, "number of solvables: %d, memory used: %d K\n", pool->nsolvables, pool->nsolvables * (int)sizeof(Solvable) / 1024);
376   POOL_DEBUG(SOLV_DEBUG_STATS, "number of ids: %d + %d\n", pool->ss.nstrings, pool->nrels);
377   POOL_DEBUG(SOLV_DEBUG_STATS, "string memory used: %d K array + %d K data,  rel memory used: %d K array\n", pool->ss.nstrings / (1024 / (int)sizeof(Id)), pool->ss.sstrings / 1024, pool->nrels * (int)sizeof(Reldep) / 1024);
378   if (pool->ss.stringhashmask || pool->relhashmask)
379     POOL_DEBUG(SOLV_DEBUG_STATS, "string hash memory: %d K, rel hash memory : %d K\n", (pool->ss.stringhashmask + 1) / (int)(1024/sizeof(Id)), (pool->relhashmask + 1) / (int)(1024/sizeof(Id)));
380
381   pool_freeidhashes(pool);      /* XXX: should not be here! */
382   pool_freewhatprovides(pool);
383   num = pool->ss.nstrings;
384   pool->whatprovides = whatprovides = solv_calloc_block(num, sizeof(Offset), WHATPROVIDES_BLOCK);
385   pool->whatprovides_rel = solv_calloc_block(pool->nrels, sizeof(Offset), WHATPROVIDES_BLOCK);
386
387   /* count providers for each name */
388   for (i = pool->nsolvables - 1; i > 0; i--)
389     {
390       Id *pp;
391       s = pool->solvables + i;
392       if (!s->provides || !s->repo || s->repo->disabled)
393         continue;
394       /* we always need the installed solvable in the whatprovides data,
395          otherwise obsoletes/conflicts on them won't work */
396       if (s->repo != installed && !pool_installable(pool, s))
397         continue;
398       pp = s->repo->idarraydata + s->provides;
399       while ((id = *pp++) != 0)
400         {
401           while (ISRELDEP(id))
402             {
403               Reldep *rd = GETRELDEP(pool, id);
404               id = rd->name;
405             }
406           whatprovides[id]++;          /* inc count of providers */
407         }
408     }
409
410   off = 2;      /* first entry is undef, second is empty list */
411   np = 0;                              /* number of names provided */
412   for (i = 0, idp = whatprovides; i < num; i++, idp++)
413     {
414       n = *idp;
415       if (!n)                          /* no providers */
416         continue;
417       off += n;                        /* make space for all providers */
418       *idp = off++;                    /* now idp points to terminating zero */
419       np++;                            /* inc # of provider 'slots' for stats */
420     }
421
422   POOL_DEBUG(SOLV_DEBUG_STATS, "provide ids: %d\n", np);
423
424   /* reserve some space for relation data */
425   extra = 2 * pool->nrels;
426   if (extra < 256)
427     extra = 256;
428
429   POOL_DEBUG(SOLV_DEBUG_STATS, "provide space needed: %d + %d\n", off, extra);
430
431   /* alloc space for all providers + extra */
432   whatprovidesdata = solv_calloc(off + extra, sizeof(Id));
433
434   /* now fill data for all provides */
435   for (i = pool->nsolvables - 1; i > 0; i--)
436     {
437       Id *pp;
438       s = pool->solvables + i;
439       if (!s->provides || !s->repo || s->repo->disabled)
440         continue;
441       if (s->repo != installed && !pool_installable(pool, s))
442         continue;
443
444       /* for all provides of this solvable */
445       pp = s->repo->idarraydata + s->provides;
446       while ((id = *pp++) != 0)
447         {
448           while (ISRELDEP(id))
449             {
450               Reldep *rd = GETRELDEP(pool, id);
451               id = rd->name;
452             }
453           d = whatprovidesdata + whatprovides[id];   /* offset into whatprovidesdata */
454           if (*d != i)          /* don't add same solvable twice */
455             {
456               d[-1] = i;
457               whatprovides[id]--;
458             }
459         }
460     }
461   pool->whatprovidesdata = whatprovidesdata;
462   pool->whatprovidesdataoff = off;
463   pool->whatprovidesdataleft = extra;
464   pool_shrink_whatprovides(pool);
465   POOL_DEBUG(SOLV_DEBUG_STATS, "whatprovides memory used: %d K id array, %d K data\n", (pool->ss.nstrings + pool->nrels + WHATPROVIDES_BLOCK) / (int)(1024/sizeof(Id)), (pool->whatprovidesdataoff + pool->whatprovidesdataleft) / (int)(1024/sizeof(Id)));
466   POOL_DEBUG(SOLV_DEBUG_STATS, "createwhatprovides took %d ms\n", solv_timems(now));
467 }
468
469 /*
470  * free all of our whatprovides data
471  * be careful, everything internalized with pool_queuetowhatprovides is
472  * gone, too
473  */
474 void
475 pool_freewhatprovides(Pool *pool)
476 {
477   pool->whatprovides = solv_free(pool->whatprovides);
478   pool->whatprovides_rel = solv_free(pool->whatprovides_rel);
479   pool->whatprovidesdata = solv_free(pool->whatprovidesdata);
480   pool->whatprovidesdataoff = 0;
481   pool->whatprovidesdataleft = 0;
482 }
483
484
485 /******************************************************************************/
486
487 /*
488  * pool_queuetowhatprovides  - add queue contents to whatprovidesdata
489  * 
490  * on-demand filling of provider information
491  * move queue data into whatprovidesdata
492  * q: queue of Ids
493  * returns: Offset into whatprovides
494  *
495  */
496 Id
497 pool_queuetowhatprovides(Pool *pool, Queue *q)
498 {
499   Offset off;
500   int count = q->count;
501
502   if (count == 0)                      /* queue empty -> 1 */
503     return 1;
504
505   /* extend whatprovidesdata if needed, +1 for ID_NULL-termination */
506   if (pool->whatprovidesdataleft < count + 1)
507     {
508       POOL_DEBUG(SOLV_DEBUG_STATS, "growing provides hash data...\n");
509       pool->whatprovidesdata = solv_realloc(pool->whatprovidesdata, (pool->whatprovidesdataoff + count + 4096) * sizeof(Id));
510       pool->whatprovidesdataleft = count + 4096;
511     }
512
513   /* copy queue to next free slot */
514   off = pool->whatprovidesdataoff;
515   memcpy(pool->whatprovidesdata + pool->whatprovidesdataoff, q->elements, count * sizeof(Id));
516
517   /* adapt count and ID_NULL-terminate */
518   pool->whatprovidesdataoff += count;
519   pool->whatprovidesdata[pool->whatprovidesdataoff++] = ID_NULL;
520   pool->whatprovidesdataleft -= count + 1;
521
522   return (Id)off;
523 }
524
525
526 /*************************************************************************/
527
528 #if defined(MULTI_SEMANTICS)
529 # define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
530 #elif defined(DEBIAN)
531 # define EVRCMP_DEPCMP EVRCMP_COMPARE
532 #else
533 # define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE
534 #endif
535
536 /* check if a package's nevr matches a dependency */
537
538 int
539 pool_match_nevr_rel(Pool *pool, Solvable *s, Id d)
540 {
541   Reldep *rd = GETRELDEP(pool, d);
542   Id name = rd->name;
543   Id evr = rd->evr;
544   int flags = rd->flags;
545
546   if (flags > 7)
547     {
548       switch (flags)
549         {
550         case REL_ARCH:
551           if (s->arch != evr)
552             return 0;
553           return pool_match_nevr(pool, s, name);
554         case REL_OR:
555           if (pool_match_nevr(pool, s, name))
556             return 1;
557           return pool_match_nevr(pool, s, evr);
558         case REL_AND:
559         case REL_WITH:
560           if (!pool_match_nevr(pool, s, name))
561             return 0;
562           return pool_match_nevr(pool, s, evr);
563         default:
564           return 0;
565         }
566     }
567   if (!pool_match_nevr(pool, s, name))
568     return 0;
569   if (evr == s->evr)
570     return (flags & REL_EQ) ? 1 : 0;
571   if (!flags)
572     return 0;
573   if (flags == 7)
574     return 1;
575   switch (pool_evrcmp(pool, s->evr, evr, EVRCMP_DEPCMP))
576     {
577     case -2:
578       return 1;
579     case -1:
580       return (flags & REL_LT) ? 1 : 0;
581     case 0:
582       return (flags & REL_EQ) ? 1 : 0;
583     case 1:
584       return (flags & REL_GT) ? 1 : 0;
585     case 2:
586       return (flags & REL_EQ) ? 1 : 0;
587     default:
588       break;
589     }
590   return 0;
591 }
592
593 /* match (flags, evr) against provider (pflags, pevr) */
594 static inline int
595 pool_match_flags_evr(Pool *pool, int pflags, Id pevr, int flags, int evr)
596 {
597   if (!pflags || !flags || pflags >= 8 || flags >= 8)
598     return 0;
599   if (flags == 7 || pflags == 7)
600     return 1;           /* rel provides every version */
601   if ((pflags & flags & (REL_LT | REL_GT)) != 0)
602     return 1;           /* both rels show in the same direction */
603   if (pevr == evr)
604     return (flags & pflags & REL_EQ) ? 1 : 0;
605   switch (pool_evrcmp(pool, pevr, evr, EVRCMP_DEPCMP))
606     {
607     case -2:
608       return (pflags & REL_EQ) ? 1 : 0;
609     case -1:
610       return (flags & REL_LT) || (pflags & REL_GT) ? 1 : 0;
611     case 0:
612       return (flags & pflags & REL_EQ) ? 1 : 0;
613     case 1:
614       return (flags & REL_GT) || (pflags & REL_LT) ? 1 : 0;
615     case 2:
616       return (flags & REL_EQ) ? 1 : 0;
617     default:
618       break;
619     }
620   return 0;
621 }
622
623 /* match two dependencies (d1 = provider) */
624
625 int
626 pool_match_dep(Pool *pool, Id d1, Id d2)
627 {
628   Reldep *rd1, *rd2;
629
630   if (d1 == d2)
631     return 1;
632   if (!ISRELDEP(d1))
633     {
634       if (!ISRELDEP(d2))
635         return 0;
636       rd2 = GETRELDEP(pool, d2);
637       return pool_match_dep(pool, d1, rd2->name);
638     }
639   rd1 = GETRELDEP(pool, d1);
640   if (!ISRELDEP(d2))
641     {
642       return pool_match_dep(pool, rd1->name, d2);
643     }
644   rd2 = GETRELDEP(pool, d2);
645   /* first match name */
646   if (!pool_match_dep(pool, rd1->name, rd2->name))
647     return 0;
648   /* name matches, check flags and evr */
649   return pool_match_flags_evr(pool, rd1->flags, rd1->evr, rd2->flags, rd2->evr);
650 }
651
652 /*
653  * addrelproviders
654  * 
655  * add packages fulfilling the relation to whatprovides array
656  * no exact providers, do range match
657  * 
658  */
659
660 Id
661 pool_addrelproviders(Pool *pool, Id d)
662 {
663   Reldep *rd = GETRELDEP(pool, d);
664   Reldep *prd;
665   Queue plist;
666   Id buf[16];
667   Id name = rd->name;
668   Id evr = rd->evr;
669   int flags = rd->flags;
670   Id pid, *pidp;
671   Id p, *pp;
672
673   d = GETRELID(d);
674   queue_init_buffer(&plist, buf, sizeof(buf)/sizeof(*buf));
675
676   if (flags >= 8)
677     {
678       /* special relation */
679       Id wp = 0;
680       Id *pp2, *pp3;
681
682       switch (flags)
683         {
684         case REL_AND:
685         case REL_WITH:
686           wp = pool_whatprovides(pool, name);
687           pp2 = pool_whatprovides_ptr(pool, evr);
688           pp = pool->whatprovidesdata + wp;
689           while ((p = *pp++) != 0)
690             {
691               for (pp3 = pp2; *pp3; pp3++)
692                 if (*pp3 == p)
693                   break;
694               if (*pp3)
695                 queue_push(&plist, p);  /* found it */
696               else
697                 wp = 0;
698             }
699           break;
700         case REL_OR:
701           wp = pool_whatprovides(pool, name);
702           pp = pool->whatprovidesdata + wp;
703           if (!*pp)
704             wp = pool_whatprovides(pool, evr);
705           else
706             {
707               int cnt;
708               while ((p = *pp++) != 0)
709                 queue_push(&plist, p);
710               cnt = plist.count;
711               pp = pool_whatprovides_ptr(pool, evr);
712               while ((p = *pp++) != 0)
713                 queue_pushunique(&plist, p);
714               if (plist.count != cnt)
715                 wp = 0;
716             }
717           break;
718         case REL_NAMESPACE:
719           if (name == NAMESPACE_OTHERPROVIDERS)
720             {
721               wp = pool_whatprovides(pool, evr);
722               break;
723             }
724           if (pool->nscallback)
725             {
726               /* ask callback which packages provide the dependency
727                * 0:  none
728                * 1:  the system (aka SYSTEMSOLVABLE)
729                * >1: set of packages, stored as offset on whatprovidesdata
730                */
731               p = pool->nscallback(pool, pool->nscallbackdata, name, evr);
732               if (p > 1)
733                 wp = p;
734               if (p == 1)
735                 queue_push(&plist, SYSTEMSOLVABLE);
736             }
737           break;
738         case REL_ARCH:
739           /* small hack: make it possible to match <pkg>.src
740            * we have to iterate over the solvables as src packages do not
741            * provide anything, thus they are not indexed in our
742            * whatprovides hash */
743           if (evr == ARCH_SRC)
744             {
745               Solvable *s;
746               for (p = 1, s = pool->solvables + p; p < pool->nsolvables; p++, s++)
747                 {
748                   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
749                     continue;
750                   if (pool_match_nevr(pool, s, name))
751                     queue_push(&plist, p);
752                 }
753               break;
754             }
755           wp = pool_whatprovides(pool, name);
756           pp = pool->whatprovidesdata + wp;
757           while ((p = *pp++) != 0)
758             {
759               Solvable *s = pool->solvables + p;
760               if (s->arch == evr)
761                 queue_push(&plist, p);
762               else
763                 wp = 0;
764             }
765           break;
766         case REL_FILECONFLICT:
767           pp = pool_whatprovides_ptr(pool, name);
768           while ((p = *pp++) != 0)
769             {
770               Id origd = MAKERELDEP(d);
771               Solvable *s = pool->solvables + p;
772               if (!s->provides)
773                 continue;
774               pidp = s->repo->idarraydata + s->provides;
775               while ((pid = *pidp++) != 0)
776                 if (pid == origd)
777                   break;
778               if (pid)
779                 queue_push(&plist, p);
780             }
781           break;
782         default:
783           break;
784         }
785       if (wp)
786         {
787           /* we can reuse an existing entry */
788           queue_free(&plist);
789           pool->whatprovides_rel[d] = wp;
790           return wp;
791         }
792     }
793   else if (flags)
794     {
795       /* simple version comparison relation */
796 #if 0
797       POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: what provides %s?\n", pool_dep2str(pool, name));
798 #endif
799       pp = pool_whatprovides_ptr(pool, name);
800       while (ISRELDEP(name))
801         {
802           rd = GETRELDEP(pool, name);
803           name = rd->name;
804         }
805       while ((p = *pp++) != 0)
806         {
807           Solvable *s = pool->solvables + p;
808           if (!s->provides)
809             {
810               /* no provides - check nevr */
811               if (pool_match_nevr_rel(pool, s, MAKERELDEP(d)))
812                 queue_push(&plist, p);
813               continue;
814             }
815           /* solvable p provides name in some rels */
816           pidp = s->repo->idarraydata + s->provides;
817           while ((pid = *pidp++) != 0)
818             {
819               if (!ISRELDEP(pid))
820                 {
821                   if (pid != name)
822                     continue;           /* wrong provides name */
823                   if (pool->disttype == DISTTYPE_DEB)
824                     continue;           /* unversioned provides can never match versioned deps */
825                   break;
826                 }
827               prd = GETRELDEP(pool, pid);
828               if (prd->name != name)
829                 continue;               /* wrong provides name */
830               /* right package, both deps are rels. check flags/evr */
831               if (pool_match_flags_evr(pool, prd->flags, prd->evr, flags, evr))
832                 break;  /* matches */
833             }
834           if (!pid)
835             continue;   /* none of the providers matched */
836           queue_push(&plist, p);
837         }
838       /* make our system solvable provide all unknown rpmlib() stuff */
839       if (plist.count == 0 && !strncmp(pool_id2str(pool, name), "rpmlib(", 7))
840         queue_push(&plist, SYSTEMSOLVABLE);
841     }
842   /* add providers to whatprovides */
843 #if 0
844   POOL_DEBUG(SOLV_DEBUG_STATS, "addrelproviders: adding %d packages to %d\n", plist.count, d);
845 #endif
846   pool->whatprovides_rel[d] = pool_queuetowhatprovides(pool, &plist);
847   queue_free(&plist);
848
849   return pool->whatprovides_rel[d];
850 }
851
852 /*************************************************************************/
853
854 void
855 pool_debug(Pool *pool, int type, const char *format, ...)
856 {
857   va_list args;
858   char buf[1024];
859
860   if ((type & (SOLV_FATAL|SOLV_ERROR)) == 0)
861     {
862       if ((pool->debugmask & type) == 0)
863         return;
864     }
865   va_start(args, format);
866   if (!pool->debugcallback)
867     {
868       if ((type & (SOLV_FATAL|SOLV_ERROR)) == 0 && !(pool->debugmask & SOLV_DEBUG_TO_STDERR))
869         vprintf(format, args);
870       else
871         vfprintf(stderr, format, args);
872       return;
873     }
874   vsnprintf(buf, sizeof(buf), format, args);
875   va_end(args);
876   pool->debugcallback(pool, pool->debugcallbackdata, type, buf);
877 }
878
879 int
880 pool_error(Pool *pool, int ret, const char *format, ...)
881 {
882   va_list args;
883   int l;
884   va_start(args, format);
885   if (!pool->errstr)
886     {
887       pool->errstra = 1024;
888       pool->errstr = solv_malloc(pool->errstra);
889     }
890   if (!*format)
891     {
892       *pool->errstr = 0;
893       l = 0;
894     }
895   else
896     l = vsnprintf(pool->errstr, pool->errstra, format, args);
897   va_end(args);
898   if (l >= 0 && l + 1 > pool->errstra)
899     {
900       pool->errstra = l + 256;
901       pool->errstr = solv_realloc(pool->errstr, pool->errstra);
902       va_start(args, format);
903       l = vsnprintf(pool->errstr, pool->errstra, format, args);
904       va_end(args);
905     }
906   if (l < 0)
907     strcpy(pool->errstr, "unknown error");
908   if (pool->debugmask & SOLV_ERROR)
909     pool_debug(pool, SOLV_ERROR, "%s\n", pool->errstr);
910   return ret;
911 }
912
913 char *
914 pool_errstr(Pool *pool)
915 {
916   return pool->errstr ? pool->errstr : "no error";
917 }
918
919 void
920 pool_setdebuglevel(Pool *pool, int level)
921 {
922   int mask = SOLV_DEBUG_RESULT;
923   if (level > 0)
924     mask |= SOLV_DEBUG_STATS|SOLV_DEBUG_ANALYZE|SOLV_DEBUG_UNSOLVABLE|SOLV_DEBUG_SOLVER|SOLV_DEBUG_TRANSACTION|SOLV_ERROR;
925   if (level > 1)
926     mask |= SOLV_DEBUG_JOB|SOLV_DEBUG_SOLUTIONS|SOLV_DEBUG_POLICY;
927   if (level > 2)
928     mask |= SOLV_DEBUG_PROPAGATE;
929   if (level > 3)
930     mask |= SOLV_DEBUG_RULE_CREATION;
931   mask |= pool->debugmask & SOLV_DEBUG_TO_STDERR;       /* keep bit */
932   pool->debugmask = mask;
933 }
934
935 void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct _Pool *, void *data, int type, const char *str), void *debugcallbackdata)
936 {
937   pool->debugcallback = debugcallback;
938   pool->debugcallbackdata = debugcallbackdata;
939 }
940
941 void pool_setdebugmask(Pool *pool, int mask)
942 {
943   pool->debugmask = mask;
944 }
945
946 void pool_setloadcallback(Pool *pool, int (*cb)(struct _Pool *, struct _Repodata *, void *), void *loadcbdata)
947 {
948   pool->loadcallback = cb;
949   pool->loadcallbackdata = loadcbdata;
950 }
951
952 /*************************************************************************/
953
954 struct searchfiles {
955   Id *ids;
956   char **dirs;
957   char **names;
958   int nfiles;
959   Map seen;
960 };
961
962 #define SEARCHFILES_BLOCK 127
963
964 static void
965 pool_addfileprovides_dep(Pool *pool, Id *ida, struct searchfiles *sf, struct searchfiles *isf)
966 {
967   Id dep, sid;
968   const char *s, *sr;
969   struct searchfiles *csf;
970
971   while ((dep = *ida++) != 0)
972     {
973       csf = sf;
974       while (ISRELDEP(dep))
975         {
976           Reldep *rd;
977           sid = pool->ss.nstrings + GETRELID(dep);
978           if (MAPTST(&csf->seen, sid))
979             {
980               dep = 0;
981               break;
982             }
983           MAPSET(&csf->seen, sid);
984           rd = GETRELDEP(pool, dep);
985           if (rd->flags < 8)
986             dep = rd->name;
987           else if (rd->flags == REL_NAMESPACE)
988             {
989               if (rd->name == NAMESPACE_INSTALLED || rd->name == NAMESPACE_SPLITPROVIDES)
990                 {
991                   csf = isf;
992                   if (!csf || MAPTST(&csf->seen, sid))
993                     {
994                       dep = 0;
995                       break;
996                     }
997                   MAPSET(&csf->seen, sid);
998                 }
999               dep = rd->evr;
1000             }
1001           else if (rd->flags == REL_FILECONFLICT)
1002             {
1003               dep = 0;
1004               break;
1005             }
1006           else
1007             {
1008               Id ids[2];
1009               ids[0] = rd->name;
1010               ids[1] = 0;
1011               pool_addfileprovides_dep(pool, ids, csf, isf);
1012               dep = rd->evr;
1013             }
1014         }
1015       if (!dep)
1016         continue;
1017       if (MAPTST(&csf->seen, dep))
1018         continue;
1019       MAPSET(&csf->seen, dep);
1020       s = pool_id2str(pool, dep);
1021       if (*s != '/')
1022         continue;
1023       csf->ids = solv_extend(csf->ids, csf->nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK);
1024       csf->dirs = solv_extend(csf->dirs, csf->nfiles, 1, sizeof(const char *), SEARCHFILES_BLOCK);
1025       csf->names = solv_extend(csf->names, csf->nfiles, 1, sizeof(const char *), SEARCHFILES_BLOCK);
1026       csf->ids[csf->nfiles] = dep;
1027       sr = strrchr(s, '/');
1028       csf->names[csf->nfiles] = solv_strdup(sr + 1);
1029       csf->dirs[csf->nfiles] = solv_malloc(sr - s + 1);
1030       if (sr != s)
1031         strncpy(csf->dirs[csf->nfiles], s, sr - s);
1032       csf->dirs[csf->nfiles][sr - s] = 0;
1033       csf->nfiles++;
1034     }
1035 }
1036
1037 struct addfileprovides_cbdata {
1038   int nfiles;
1039   Id *ids;
1040   char **dirs;
1041   char **names;
1042
1043   Id *dids;
1044
1045   Map providedids;
1046
1047   Map useddirs;
1048 };
1049
1050 static int
1051 addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
1052 {
1053   struct addfileprovides_cbdata *cbd = cbdata;
1054   int i;
1055
1056   if (!cbd->useddirs.size)
1057     {
1058       map_init(&cbd->useddirs, data->dirpool.ndirs + 1);
1059       for (i = 0; i < cbd->nfiles; i++)
1060         {
1061           Id did;
1062           if (MAPTST(&cbd->providedids, cbd->ids[i]))
1063             {
1064               cbd->dids[i] = 0;
1065               continue;
1066             }
1067           did = repodata_str2dir(data, cbd->dirs[i], 0);
1068           cbd->dids[i] = did;
1069           if (did)
1070             MAPSET(&cbd->useddirs, did);
1071         }
1072       repodata_free_dircache(data);
1073     }
1074   if (value->id >= data->dirpool.ndirs || !MAPTST(&cbd->useddirs, value->id))
1075     return 0;
1076   for (i = 0; i < cbd->nfiles; i++)
1077     {
1078       if (cbd->dids[i] != value->id)
1079         continue;
1080       if (!strcmp(cbd->names[i], value->str))
1081         break;
1082     }
1083   if (i == cbd->nfiles)
1084     return 0;
1085   s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER);
1086   return 0;
1087 }
1088
1089 static void
1090 pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly)
1091 {
1092   Id p;
1093   Repodata *data;
1094   Repo *repo;
1095   Queue fileprovidesq;
1096   int i, j, repoid, repodataid;
1097   int provstart, provend;
1098   Map donemap;
1099   int ndone, incomplete;
1100
1101   if (!pool->urepos)
1102     return;
1103
1104   cbd->nfiles = sf->nfiles;
1105   cbd->ids = sf->ids;
1106   cbd->dirs = sf->dirs;
1107   cbd->names = sf->names;
1108   cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id));
1109   map_init(&cbd->providedids, pool->ss.nstrings);
1110
1111   repoid = 1;
1112   repo = repoonly ? repoonly : pool->repos[repoid];
1113   map_init(&donemap, pool->nsolvables);
1114   queue_init(&fileprovidesq);
1115   provstart = provend = 0;
1116   for (;;)
1117     {
1118       if (!repo || repo->disabled)
1119         {
1120           if (repoonly || ++repoid == pool->nrepos)
1121             break;
1122           repo = pool->repos[repoid];
1123           continue;
1124         }
1125       ndone = 0;
1126       FOR_REPODATAS(repo, repodataid, data)
1127         {
1128           if (ndone >= repo->nsolvables)
1129             break;
1130
1131           if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
1132             {
1133               map_empty(&cbd->providedids);
1134               for (i = 0; i < fileprovidesq.count; i++)
1135                 MAPSET(&cbd->providedids, fileprovidesq.elements[i]);
1136               provstart = data->start;
1137               provend = data->end;
1138               for (i = 0; i < cbd->nfiles; i++)
1139                 if (!MAPTST(&cbd->providedids, cbd->ids[i]))
1140                   break;
1141               if (i == cbd->nfiles)
1142                 {
1143                   /* great! no need to search files */
1144                   for (p = data->start; p < data->end; p++)
1145                     if (pool->solvables[p].repo == repo)
1146                       {
1147                         if (MAPTST(&donemap, p))
1148                           continue;
1149                         MAPSET(&donemap, p);
1150                         ndone++;
1151                       }
1152                   continue;
1153                 }
1154             }
1155
1156           if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
1157             continue;
1158
1159           if (data->start < provstart || data->end > provend)
1160             {
1161               map_empty(&cbd->providedids);
1162               provstart = provend = 0;
1163             }
1164
1165           /* check if the data is incomplete */
1166           incomplete = 0;
1167           if (data->state == REPODATA_AVAILABLE)
1168             {
1169               for (j = 1; j < data->nkeys; j++)
1170                 if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1171                   break;
1172               if (j < data->nkeys)
1173                 {
1174 #if 0
1175                   for (i = 0; i < cbd->nfiles; i++)
1176                     if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
1177                       printf("need complete filelist because of %s\n", pool_id2str(pool, cbd->ids[i]));
1178 #endif
1179                   for (i = 0; i < cbd->nfiles; i++)
1180                     if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
1181                       break;
1182                   if (i < cbd->nfiles)
1183                     incomplete = 1;
1184                 }
1185             }
1186
1187           /* do the search */
1188           map_init(&cbd->useddirs, 0);
1189           for (p = data->start; p < data->end; p++)
1190             if (pool->solvables[p].repo == repo)
1191               {
1192                 if (MAPTST(&donemap, p))
1193                   continue;
1194                 repodata_search(data, p, SOLVABLE_FILELIST, 0, addfileprovides_cb, cbd);
1195                 if (!incomplete)
1196                   {
1197                     MAPSET(&donemap, p);
1198                     ndone++;
1199                   }
1200               }
1201           map_free(&cbd->useddirs);
1202         }
1203
1204       if (repoonly || ++repoid == pool->nrepos)
1205         break;
1206       repo = pool->repos[repoid];
1207     }
1208   map_free(&donemap);
1209   queue_free(&fileprovidesq);
1210   map_free(&cbd->providedids);
1211 }
1212
1213 void
1214 pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
1215 {
1216   Solvable *s;
1217   Repo *installed, *repo;
1218   struct searchfiles sf, isf, *isfp;
1219   struct addfileprovides_cbdata cbd;
1220   int i;
1221   unsigned int now;
1222
1223   installed = pool->installed;
1224   now = solv_timems(0);
1225   memset(&sf, 0, sizeof(sf));
1226   map_init(&sf.seen, pool->ss.nstrings + pool->nrels);
1227   memset(&isf, 0, sizeof(isf));
1228   map_init(&isf.seen, pool->ss.nstrings + pool->nrels);
1229
1230   if (idq)
1231     queue_empty(idq);
1232   if (idqinst)
1233     queue_empty(idqinst);
1234   isfp = installed ? &isf : 0;
1235   for (i = 1, s = pool->solvables + i; i < pool->nsolvables; i++, s++)
1236     {
1237       repo = s->repo;
1238       if (!repo)
1239         continue;
1240       if (s->obsoletes)
1241         pool_addfileprovides_dep(pool, repo->idarraydata + s->obsoletes, &sf, isfp);
1242       if (s->conflicts)
1243         pool_addfileprovides_dep(pool, repo->idarraydata + s->conflicts, &sf, isfp);
1244       if (s->requires)
1245         pool_addfileprovides_dep(pool, repo->idarraydata + s->requires, &sf, isfp);
1246       if (s->recommends)
1247         pool_addfileprovides_dep(pool, repo->idarraydata + s->recommends, &sf, isfp);
1248       if (s->suggests)
1249         pool_addfileprovides_dep(pool, repo->idarraydata + s->suggests, &sf, isfp);
1250       if (s->supplements)
1251         pool_addfileprovides_dep(pool, repo->idarraydata + s->supplements, &sf, isfp);
1252       if (s->enhances)
1253         pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp);
1254     }
1255   map_free(&sf.seen);
1256   map_free(&isf.seen);
1257   POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles);
1258   cbd.dids = 0;
1259   if (sf.nfiles)
1260     {
1261 #if 0
1262       for (i = 0; i < sf.nfiles; i++)
1263         POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i]));
1264 #endif
1265       pool_addfileprovides_search(pool, &cbd, &sf, 0);
1266       if (idq)
1267         for (i = 0; i < sf.nfiles; i++)
1268           queue_push(idq, sf.ids[i]);
1269       if (idqinst)
1270         for (i = 0; i < sf.nfiles; i++)
1271           queue_push(idqinst, sf.ids[i]);
1272       solv_free(sf.ids);
1273       for (i = 0; i < sf.nfiles; i++)
1274         {
1275           solv_free(sf.dirs[i]);
1276           solv_free(sf.names[i]);
1277         }
1278       solv_free(sf.dirs);
1279       solv_free(sf.names);
1280     }
1281   if (isf.nfiles)
1282     {
1283 #if 0
1284       for (i = 0; i < isf.nfiles; i++)
1285         POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i]));
1286 #endif
1287       if (installed)
1288         pool_addfileprovides_search(pool, &cbd, &isf, installed);
1289       if (installed && idqinst)
1290         for (i = 0; i < isf.nfiles; i++)
1291           queue_pushunique(idqinst, isf.ids[i]);
1292       solv_free(isf.ids);
1293       for (i = 0; i < isf.nfiles; i++)
1294         {
1295           solv_free(isf.dirs[i]);
1296           solv_free(isf.names[i]);
1297         }
1298       solv_free(isf.dirs);
1299       solv_free(isf.names);
1300     }
1301   solv_free(cbd.dids);
1302   pool_freewhatprovides(pool);  /* as we have added provides */
1303   POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now));
1304 }
1305
1306 void
1307 pool_addfileprovides(Pool *pool)
1308 {
1309   pool_addfileprovides_queue(pool, 0, 0);
1310 }
1311
1312 void
1313 pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct _Repodata *data, struct _Repokey *key, struct _KeyValue *kv), void *cbdata)
1314 {
1315   if (p)
1316     {
1317       if (pool->solvables[p].repo)
1318         repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata);
1319       return;
1320     }
1321   /* FIXME: obey callback return value! */
1322   for (p = 1; p < pool->nsolvables; p++)
1323     if (pool->solvables[p].repo)
1324       repo_search(pool->solvables[p].repo, p, key, match, flags, callback, cbdata);
1325 }
1326
1327 void
1328 pool_clear_pos(Pool *pool)
1329 {
1330   memset(&pool->pos, 0, sizeof(pool->pos));
1331 }
1332
1333
1334 void
1335 pool_set_languages(Pool *pool, const char **languages, int nlanguages)
1336 {
1337   int i;
1338
1339   pool->languagecache = solv_free(pool->languagecache);
1340   pool->languagecacheother = 0;
1341   if (pool->nlanguages)
1342     {
1343       for (i = 0; i < pool->nlanguages; i++)
1344         free((char *)pool->languages[i]);
1345       free(pool->languages);
1346     }
1347   pool->nlanguages = nlanguages;
1348   if (!nlanguages)
1349     return;
1350   pool->languages = solv_calloc(nlanguages, sizeof(const char **));
1351   for (i = 0; i < pool->nlanguages; i++)
1352     pool->languages[i] = solv_strdup(languages[i]);
1353 }
1354
1355 Id
1356 pool_id2langid(Pool *pool, Id id, const char *lang, int create)
1357 {
1358   const char *n;
1359   char buf[256], *p;
1360   int l;
1361
1362   if (!lang || !*lang)
1363     return id;
1364   n = pool_id2str(pool, id);
1365   l = strlen(n) + strlen(lang) + 2;
1366   if (l > sizeof(buf))
1367     p = solv_malloc(strlen(n) + strlen(lang) + 2);
1368   else
1369     p = buf;
1370   sprintf(p, "%s:%s", n, lang);
1371   id = pool_str2id(pool, p, create);
1372   if (p != buf)
1373     free(p);
1374   return id;
1375 }
1376
1377 char *
1378 pool_alloctmpspace(Pool *pool, int len)
1379 {
1380   int n = pool->tmpspace.n;
1381   if (!len)
1382     return 0;
1383   if (len > pool->tmpspace.len[n])
1384     {
1385       pool->tmpspace.buf[n] = solv_realloc(pool->tmpspace.buf[n], len + 32);
1386       pool->tmpspace.len[n] = len + 32;
1387     }
1388   pool->tmpspace.n = (n + 1) % POOL_TMPSPACEBUF;
1389   return pool->tmpspace.buf[n];
1390 }
1391
1392 static char *
1393 pool_alloctmpspace_free(Pool *pool, const char *space, int len)
1394 {
1395   if (space)
1396     {
1397       int n, oldn;
1398       n = oldn = pool->tmpspace.n;
1399       for (;;)
1400         {
1401           if (!n--)
1402             n = POOL_TMPSPACEBUF - 1;
1403           if (n == oldn)
1404             break;
1405           if (pool->tmpspace.buf[n] != space)
1406             continue;
1407           if (len > pool->tmpspace.len[n])
1408             {
1409               pool->tmpspace.buf[n] = solv_realloc(pool->tmpspace.buf[n], len + 32);
1410               pool->tmpspace.len[n] = len + 32;
1411             }
1412           return pool->tmpspace.buf[n];
1413         }
1414     }
1415   return 0;
1416 }
1417
1418 void
1419 pool_freetmpspace(Pool *pool, const char *space)
1420 {
1421   int n = pool->tmpspace.n;
1422   if (!space)
1423     return;
1424   n = (n + (POOL_TMPSPACEBUF - 1)) % POOL_TMPSPACEBUF;
1425   if (pool->tmpspace.buf[n] == space)
1426     pool->tmpspace.n = n;
1427 }
1428
1429 char *
1430 pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3)
1431 {
1432   int l1, l2, l3;
1433   char *s, *str;
1434   l1 = str1 ? strlen(str1) : 0;
1435   l2 = str2 ? strlen(str2) : 0;
1436   l3 = str3 ? strlen(str3) : 0;
1437   s = str = pool_alloctmpspace(pool, l1 + l2 + l3 + 1);
1438   if (l1)
1439     {
1440       strcpy(s, str1);
1441       s += l1;
1442     }
1443   if (l2)
1444     {
1445       strcpy(s, str2);
1446       s += l2;
1447     }
1448   if (l3)
1449     {
1450       strcpy(s, str3);
1451       s += l3;
1452     }
1453   *s = 0;
1454   return str;
1455 }
1456
1457 char *
1458 pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3)
1459 {
1460   int l1, l2, l3;
1461   char *s, *str;
1462
1463   l1 = str1 ? strlen(str1) : 0;
1464   l2 = str2 ? strlen(str2) : 0;
1465   l3 = str3 ? strlen(str3) : 0;
1466   str = pool_alloctmpspace_free(pool, str1, l1 + l2 + l3 + 1);
1467   if (str)
1468     str1 = str;
1469   else
1470     str = pool_alloctmpspace(pool, l1 + l2 + l3 + 1);
1471   s = str;
1472   if (l1)
1473     {
1474       if (s != str1)
1475         strcpy(s, str1);
1476       s += l1;
1477     }
1478   if (l2)
1479     {
1480       strcpy(s, str2);
1481       s += l2;
1482     }
1483   if (l3)
1484     {
1485       strcpy(s, str3);
1486       s += l3;
1487     }
1488   *s = 0;
1489   return str;
1490 }
1491
1492 const char *
1493 pool_bin2hex(Pool *pool, const unsigned char *buf, int len)
1494 {
1495   char *s;
1496   if (!len)
1497     return "";
1498   s = pool_alloctmpspace(pool, 2 * len + 1);
1499   solv_bin2hex(buf, len, s);
1500   return s;
1501 }
1502
1503 /*******************************************************************/
1504
1505 struct mptree {
1506   Id sibling;
1507   Id child;
1508   const char *comp;
1509   int compl;
1510   Id mountpoint;
1511 };
1512
1513 struct ducbdata {
1514   DUChanges *mps;
1515   struct mptree *mptree;
1516   int addsub;
1517   int hasdu;
1518
1519   Id *dirmap;
1520   int nmap;
1521   Repodata *olddata;
1522 };
1523
1524
1525 static int
1526 solver_fill_DU_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
1527 {
1528   struct ducbdata *cbd = cbdata;
1529   Id mp;
1530
1531   if (data != cbd->olddata)
1532     {
1533       Id dn, mp, comp, *dirmap, *dirs;
1534       int i, compl;
1535       const char *compstr;
1536       struct mptree *mptree;
1537
1538       /* create map from dir to mptree */
1539       cbd->dirmap = solv_free(cbd->dirmap);
1540       cbd->nmap = 0;
1541       dirmap = solv_calloc(data->dirpool.ndirs, sizeof(Id));
1542       mptree = cbd->mptree;
1543       mp = 0;
1544       for (dn = 2, dirs = data->dirpool.dirs + dn; dn < data->dirpool.ndirs; dn++)
1545         {
1546           comp = *dirs++;
1547           if (comp <= 0)
1548             {
1549               mp = dirmap[-comp];
1550               continue;
1551             }
1552           if (mp < 0)
1553             {
1554               /* unconnected */
1555               dirmap[dn] = mp;
1556               continue;
1557             }
1558           if (!mptree[mp].child)
1559             {
1560               dirmap[dn] = -mp;
1561               continue;
1562             }
1563           if (data->localpool)
1564             compstr = stringpool_id2str(&data->spool, comp);
1565           else
1566             compstr = pool_id2str(data->repo->pool, comp);
1567           compl = strlen(compstr);
1568           for (i = mptree[mp].child; i; i = mptree[i].sibling)
1569             if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
1570               break;
1571           dirmap[dn] = i ? i : -mp;
1572         }
1573       /* change dirmap to point to mountpoint instead of mptree */
1574       for (dn = 0; dn < data->dirpool.ndirs; dn++)
1575         {
1576           mp = dirmap[dn];
1577           dirmap[dn] = mptree[mp > 0 ? mp : -mp].mountpoint;
1578         }
1579       cbd->dirmap = dirmap;
1580       cbd->nmap = data->dirpool.ndirs;
1581       cbd->olddata = data;
1582     }
1583   cbd->hasdu = 1;
1584   if (value->id < 0 || value->id >= cbd->nmap)
1585     return 0;
1586   mp = cbd->dirmap[value->id];
1587   if (mp < 0)
1588     return 0;
1589   if (cbd->addsub > 0)
1590     {
1591       cbd->mps[mp].kbytes += value->num;
1592       cbd->mps[mp].files += value->num2;
1593     }
1594   else
1595     {
1596       cbd->mps[mp].kbytes -= value->num;
1597       cbd->mps[mp].files -= value->num2;
1598     }
1599   return 0;
1600 }
1601
1602 static void
1603 propagate_mountpoints(struct mptree *mptree, int pos, Id mountpoint)
1604 {
1605   int i;
1606   if (mptree[pos].mountpoint == -1)
1607     mptree[pos].mountpoint = mountpoint;
1608   else
1609     mountpoint = mptree[pos].mountpoint;
1610   for (i = mptree[pos].child; i; i = mptree[i].sibling)
1611     propagate_mountpoints(mptree, i, mountpoint);
1612 }
1613
1614 #define MPTREE_BLOCK 15
1615
1616 void
1617 pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps)
1618 {
1619   char *p;
1620   const char *path, *compstr;
1621   struct mptree *mptree;
1622   int i, nmptree;
1623   int pos, compl;
1624   int mp;
1625   struct ducbdata cbd;
1626   Solvable *s;
1627   Id sp;
1628   Map ignoredu;
1629   Repo *oldinstalled = pool->installed;
1630
1631   memset(&ignoredu, 0, sizeof(ignoredu));
1632   cbd.mps = mps;
1633   cbd.addsub = 0;
1634   cbd.dirmap = 0;
1635   cbd.nmap = 0;
1636   cbd.olddata = 0;
1637
1638   mptree = solv_extend_resize(0, 1, sizeof(struct mptree), MPTREE_BLOCK);
1639
1640   /* our root node */
1641   mptree[0].sibling = 0;
1642   mptree[0].child = 0;
1643   mptree[0].comp = 0;
1644   mptree[0].compl = 0;
1645   mptree[0].mountpoint = -1;
1646   nmptree = 1;
1647   
1648   /* create component tree */
1649   for (mp = 0; mp < nmps; mp++)
1650     {
1651       mps[mp].kbytes = 0;
1652       mps[mp].files = 0;
1653       pos = 0;
1654       path = mps[mp].path;
1655       while(*path == '/')
1656         path++;
1657       while (*path)
1658         {
1659           if ((p = strchr(path, '/')) == 0)
1660             {
1661               compstr = path;
1662               compl = strlen(compstr);
1663               path += compl;
1664             }
1665           else
1666             {
1667               compstr = path;
1668               compl = p - path;
1669               path = p + 1;
1670               while(*path == '/')
1671                 path++;
1672             }
1673           for (i = mptree[pos].child; i; i = mptree[i].sibling)
1674             if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
1675               break;
1676           if (!i)
1677             {
1678               /* create new node */
1679               mptree = solv_extend(mptree, nmptree, 1, sizeof(struct mptree), MPTREE_BLOCK);
1680               i = nmptree++;
1681               mptree[i].sibling = mptree[pos].child;
1682               mptree[i].child = 0;
1683               mptree[i].comp = compstr;
1684               mptree[i].compl = compl;
1685               mptree[i].mountpoint = -1;
1686               mptree[pos].child = i;
1687             }
1688           pos = i;
1689         }
1690       mptree[pos].mountpoint = mp;
1691     }
1692
1693   propagate_mountpoints(mptree, 0, mptree[0].mountpoint);
1694
1695 #if 0
1696   for (i = 0; i < nmptree; i++)
1697     {
1698       printf("#%d sibling: %d\n", i, mptree[i].sibling);
1699       printf("#%d child: %d\n", i, mptree[i].child);
1700       printf("#%d comp: %s\n", i, mptree[i].comp);
1701       printf("#%d compl: %d\n", i, mptree[i].compl);
1702       printf("#%d mountpont: %d\n", i, mptree[i].mountpoint);
1703     }
1704 #endif
1705
1706   cbd.mptree = mptree;
1707   cbd.addsub = 1;
1708   for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
1709     {
1710       if (!s->repo || (oldinstalled && s->repo == oldinstalled))
1711         continue;
1712       if (!MAPTST(installedmap, sp))
1713         continue;
1714       cbd.hasdu = 0;
1715       repo_search(s->repo, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
1716       if (!cbd.hasdu && oldinstalled)
1717         {
1718           Id op, opp;
1719           /* no du data available, ignore data of all installed solvables we obsolete */
1720           if (!ignoredu.map)
1721             map_init(&ignoredu, oldinstalled->end - oldinstalled->start);
1722           if (s->obsoletes)
1723             {
1724               Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
1725               while ((obs = *obsp++) != 0)
1726                 FOR_PROVIDES(op, opp, obs)
1727                   if (op >= oldinstalled->start && op < oldinstalled->end)
1728                     MAPSET(&ignoredu, op - oldinstalled->start);
1729             }
1730           FOR_PROVIDES(op, opp, s->name)
1731             if (pool->solvables[op].name == s->name)
1732               if (op >= oldinstalled->start && op < oldinstalled->end)
1733                 MAPSET(&ignoredu, op - oldinstalled->start);
1734         }
1735     }
1736   cbd.addsub = -1;
1737   if (oldinstalled)
1738     {
1739       /* assumes we allways have du data for installed solvables */
1740       FOR_REPO_SOLVABLES(oldinstalled, sp, s)
1741         {
1742           if (MAPTST(installedmap, sp))
1743             continue;
1744           if (ignoredu.map && MAPTST(&ignoredu, sp - oldinstalled->start))
1745             continue;
1746           repo_search(oldinstalled, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
1747         }
1748     }
1749   if (ignoredu.map)
1750     map_free(&ignoredu);
1751   solv_free(cbd.dirmap);
1752   solv_free(mptree);
1753 }
1754
1755 int
1756 pool_calc_installsizechange(Pool *pool, Map *installedmap)
1757 {
1758   Id sp;
1759   Solvable *s;
1760   int change = 0;
1761   Repo *oldinstalled = pool->installed;
1762
1763   for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
1764     {
1765       if (!s->repo || (oldinstalled && s->repo == oldinstalled))
1766         continue;
1767       if (!MAPTST(installedmap, sp))
1768         continue;
1769       change += solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
1770     }
1771   if (oldinstalled)
1772     {
1773       FOR_REPO_SOLVABLES(oldinstalled, sp, s)
1774         {
1775           if (MAPTST(installedmap, sp))
1776             continue;
1777           change -= solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
1778         }
1779     }
1780   return change;
1781 }
1782
1783 /* map:
1784  *  1: installed
1785  *  2: conflicts with installed
1786  *  8: interesting (only true if installed)
1787  * 16: undecided
1788  */
1789  
1790 static inline Id dep2name(Pool *pool, Id dep)
1791 {
1792   while (ISRELDEP(dep))
1793     {
1794       Reldep *rd = rd = GETRELDEP(pool, dep);
1795       dep = rd->name;
1796     }
1797   return dep;
1798 }
1799
1800 static int providedbyinstalled_multiversion(Pool *pool, unsigned char *map, Id n, Id con) 
1801 {
1802   Id p, pp;
1803   Solvable *sn = pool->solvables + n; 
1804
1805   FOR_PROVIDES(p, pp, sn->name)
1806     {    
1807       Solvable *s = pool->solvables + p; 
1808       if (s->name != sn->name || s->arch != sn->arch)
1809         continue;
1810       if ((map[p] & 9) != 9)
1811         continue;
1812       if (pool_match_nevr(pool, pool->solvables + p, con))
1813         continue;
1814       return 1;         /* found installed package that doesn't conflict */
1815     }
1816   return 0;
1817 }
1818
1819 static inline int providedbyinstalled(Pool *pool, unsigned char *map, Id dep, int ispatch, Map *noobsoletesmap)
1820 {
1821   Id p, pp;
1822   int r = 0;
1823   FOR_PROVIDES(p, pp, dep)
1824     {
1825       if (p == SYSTEMSOLVABLE)
1826         return 1;       /* always boring, as never constraining */
1827       if (ispatch && !pool_match_nevr(pool, pool->solvables + p, dep))
1828         continue;
1829       if (ispatch && noobsoletesmap && noobsoletesmap->size && MAPTST(noobsoletesmap, p) && ISRELDEP(dep))
1830         if (providedbyinstalled_multiversion(pool, map, p, dep))
1831           continue;
1832       if ((map[p] & 9) == 9)
1833         return 9;
1834       r |= map[p] & 17;
1835     }
1836   return r;
1837 }
1838
1839 /*
1840  * pool_trivial_installable - calculate if a set of solvables is
1841  * trivial installable without any other installs/deinstalls of
1842  * packages not belonging to the set.
1843  *
1844  * the state is returned in the result queue:
1845  * 1:  solvable is installable without any other package changes
1846  * 0:  solvable is not installable
1847  * -1: solvable is installable, but doesn't constrain any installed packages
1848  */
1849
1850 void
1851 pool_trivial_installable_noobsoletesmap(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res, Map *noobsoletesmap)
1852 {
1853   int i, r, m, did;
1854   Id p, *dp, con, *conp, req, *reqp;
1855   unsigned char *map;
1856   Solvable *s;
1857
1858   map = solv_calloc(pool->nsolvables, 1);
1859   for (p = 1; p < pool->nsolvables; p++)
1860     {
1861       if (!MAPTST(installedmap, p))
1862         continue;
1863       map[p] |= 9;
1864       s = pool->solvables + p;
1865       if (!s->conflicts)
1866         continue;
1867       conp = s->repo->idarraydata + s->conflicts;
1868       while ((con = *conp++) != 0)
1869         {
1870           dp = pool_whatprovides_ptr(pool, con);
1871           for (; *dp; dp++)
1872             map[p] |= 2;        /* XXX: self conflict ? */
1873         }
1874     }
1875   for (i = 0; i < pkgs->count; i++)
1876     map[pkgs->elements[i]] = 16;
1877
1878   for (i = 0, did = 0; did < pkgs->count; i++, did++)
1879     {
1880       if (i == pkgs->count)
1881         i = 0;
1882       p = pkgs->elements[i];
1883       if ((map[p] & 16) == 0)
1884         continue;
1885       if ((map[p] & 2) != 0)
1886         {
1887           map[p] = 2;
1888           continue;
1889         }
1890       s = pool->solvables + p;
1891       m = 1;
1892       if (s->requires)
1893         {
1894           reqp = s->repo->idarraydata + s->requires;
1895           while ((req = *reqp++) != 0)
1896             {
1897               if (req == SOLVABLE_PREREQMARKER)
1898                 continue;
1899               r = providedbyinstalled(pool, map, req, 0, 0);
1900               if (!r)
1901                 {
1902                   /* decided and miss */
1903                   map[p] = 2;
1904                   break;
1905                 }
1906               m |= r;   /* 1 | 9 | 16 | 17 */
1907             }
1908           if (req)
1909             continue;
1910           if ((m & 9) == 9)
1911             m = 9;
1912         }
1913       if (s->conflicts)
1914         {
1915           int ispatch = 0;      /* see solver.c patch handling */
1916
1917           if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
1918             ispatch = 1;
1919           conp = s->repo->idarraydata + s->conflicts;
1920           while ((con = *conp++) != 0)
1921             {
1922               if ((providedbyinstalled(pool, map, con, ispatch, noobsoletesmap) & 1) != 0)
1923                 {
1924                   map[p] = 2;
1925                   break;
1926                 }
1927               if ((m == 1 || m == 17) && ISRELDEP(con))
1928                 {
1929                   con = dep2name(pool, con);
1930                   if ((providedbyinstalled(pool, map, con, ispatch, noobsoletesmap) & 1) != 0)
1931                     m = 9;
1932                 }
1933             }
1934           if (con)
1935             continue;   /* found a conflict */
1936         }
1937 #if 0
1938       if (s->repo && s->repo != oldinstalled)
1939         {
1940           Id p2, obs, *obsp, *pp;
1941           Solvable *s2;
1942           if (s->obsoletes)
1943             {
1944               obsp = s->repo->idarraydata + s->obsoletes;
1945               while ((obs = *obsp++) != 0)
1946                 {
1947                   if ((providedbyinstalled(pool, map, obs, 0, 0) & 1) != 0)
1948                     {
1949                       map[p] = 2;
1950                       break;
1951                     }
1952                 }
1953               if (obs)
1954                 continue;
1955             }
1956           FOR_PROVIDES(p2, pp, s->name)
1957             {
1958               s2 = pool->solvables + p2;
1959               if (s2->name == s->name && (map[p2] & 1) != 0)
1960                 {
1961                   map[p] = 2;
1962                   break;
1963                 }
1964             }
1965           if (p2)
1966             continue;
1967         }
1968 #endif
1969       if (m != map[p])
1970         {
1971           map[p] = m;
1972           did = 0;
1973         }
1974     }
1975   queue_free(res);
1976   queue_init_clone(res, pkgs);
1977   for (i = 0; i < pkgs->count; i++)
1978     {
1979       m = map[pkgs->elements[i]];
1980       if ((m & 9) == 9)
1981         r = 1;
1982       else if (m & 1)
1983         r = -1;
1984       else
1985         r = 0;
1986       res->elements[i] = r;
1987     }
1988   free(map);
1989 }
1990
1991 void
1992 pool_trivial_installable(Pool *pool, Map *installedmap, Queue *pkgs, Queue *res)
1993 {
1994   pool_trivial_installable_noobsoletesmap(pool, installedmap, pkgs, res, 0);
1995 }
1996
1997 const char *
1998 pool_lookup_str(Pool *pool, Id entry, Id keyname)
1999 {
2000   if (entry == SOLVID_POS && pool->pos.repo)
2001     return repodata_lookup_str(pool->pos.repo->repodata + pool->pos.repodataid, SOLVID_POS, keyname);
2002   if (entry <= 0)
2003     return 0;
2004   return solvable_lookup_str(pool->solvables + entry, keyname);
2005 }
2006
2007 Id
2008 pool_lookup_id(Pool *pool, Id entry, Id keyname)
2009 {
2010   if (entry == SOLVID_POS && pool->pos.repo)
2011     {
2012       Repodata *data = pool->pos.repo->repodata + pool->pos.repodataid;
2013       Id id = repodata_lookup_id(data, SOLVID_POS, keyname);
2014       return data->localpool ? repodata_globalize_id(data, id, 1) : id;
2015     }
2016   if (entry <= 0)
2017     return 0;
2018   return solvable_lookup_id(pool->solvables + entry, keyname);
2019 }
2020
2021 unsigned long long
2022 pool_lookup_num(Pool *pool, Id entry, Id keyname, unsigned long long notfound)
2023 {
2024   if (entry == SOLVID_POS && pool->pos.repo)
2025     {
2026       unsigned long long value;
2027       if (repodata_lookup_num(pool->pos.repo->repodata + pool->pos.repodataid, SOLVID_POS, keyname, &value))
2028         return value;
2029       return notfound;
2030     }
2031   if (entry <= 0)
2032     return notfound;
2033   return solvable_lookup_num(pool->solvables + entry, keyname, notfound);
2034 }
2035
2036 int
2037 pool_lookup_void(Pool *pool, Id entry, Id keyname)
2038 {
2039   if (entry == SOLVID_POS && pool->pos.repo)
2040     return repodata_lookup_void(pool->pos.repo->repodata + pool->pos.repodataid, SOLVID_POS, keyname);
2041   if (entry <= 0)
2042     return 0;
2043   return solvable_lookup_void(pool->solvables + entry, keyname);
2044 }
2045
2046 const unsigned char *
2047 pool_lookup_bin_checksum(Pool *pool, Id entry, Id keyname, Id *typep)
2048 {
2049   if (entry == SOLVID_POS && pool->pos.repo)
2050     return repodata_lookup_bin_checksum(pool->pos.repo->repodata + pool->pos.repodataid, SOLVID_POS, keyname, typep);
2051   if (entry <= 0)
2052     return 0;
2053   return solvable_lookup_bin_checksum(pool->solvables + entry, keyname, typep);
2054 }
2055
2056 const char *
2057 pool_lookup_checksum(Pool *pool, Id entry, Id keyname, Id *typep)
2058 {
2059   if (entry == SOLVID_POS && pool->pos.repo)
2060     {
2061       const unsigned char *chk = repodata_lookup_bin_checksum(pool->pos.repo->repodata + pool->pos.repodataid, SOLVID_POS, keyname, typep);
2062       return chk ? repodata_chk2str(pool->pos.repo->repodata + pool->pos.repodataid, *typep, chk) : 0;
2063     }
2064   if (entry <= 0)
2065     return 0;
2066   return solvable_lookup_checksum(pool->solvables + entry, keyname, typep);
2067 }
2068
2069 void
2070 pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts)
2071 {
2072   int hadhashes = pool->relhashtbl ? 1 : 0;
2073   Solvable *s;
2074   Id fn, p, q, md5;
2075   Id id;
2076   int i;
2077
2078   if (!conflicts->count)
2079     return;
2080   pool_freewhatprovides(pool);
2081   for (i = 0; i < conflicts->count; i += 5)
2082     {
2083       fn = conflicts->elements[i];
2084       p = conflicts->elements[i + 1];
2085       md5 = conflicts->elements[i + 2];
2086       q = conflicts->elements[i + 3];
2087       id = pool_rel2id(pool, fn, md5, REL_FILECONFLICT, 1);
2088       s = pool->solvables + p;
2089       if (!s->repo)
2090         continue;
2091       s->provides = repo_addid_dep(s->repo, s->provides, id, SOLVABLE_FILEMARKER);
2092       s = pool->solvables + q;
2093       if (!s->repo)
2094         continue;
2095       s->conflicts = repo_addid_dep(s->repo, s->conflicts, id, 0);
2096     }
2097   if (!hadhashes)
2098     pool_freeidhashes(pool);
2099 }
2100
2101 /* EOF */