Upload Tizen:Main source
[external/libsatsolver.git] / src / repo.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  * repo.c
10  *
11  * Manage metadata coming from one repository
12  *
13  */
14
15 #define _GNU_SOURCE
16 #include <string.h>
17 #include <fnmatch.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21
22
23
24 #include "repo.h"
25 #include "pool.h"
26 #include "poolid_private.h"
27 #include "util.h"
28 #include "chksum.h"
29
30 #define IDARRAY_BLOCK     4095
31
32
33 /*
34  * create empty repo
35  * and add to pool
36  */
37
38 Repo *
39 repo_create(Pool *pool, const char *name)
40 {
41   Repo *repo;
42
43   pool_freewhatprovides(pool);
44   repo = (Repo *)sat_calloc(1, sizeof(*repo));
45   pool->repos = (Repo **)sat_realloc2(pool->repos, pool->nrepos + 1, sizeof(Repo *));
46   pool->repos[pool->nrepos++] = repo;
47   repo->repoid = pool->nrepos;
48   repo->name = name ? strdup(name) : 0;
49   repo->pool = pool;
50   repo->start = pool->nsolvables;
51   repo->end = pool->nsolvables;
52   repo->nsolvables = 0;
53   return repo;
54 }
55
56 static void
57 repo_freedata(Repo *repo)
58 {
59   int i;
60   for (i = 0; i < repo->nrepodata; i++)
61     repodata_freedata(repo->repodata + i);
62   sat_free(repo->repodata);
63   sat_free(repo->idarraydata);
64   sat_free(repo->rpmdbid);
65   sat_free((char *)repo->name);
66   sat_free(repo);
67 }
68
69 /* delete all solvables and repodata blocks from this repo */
70
71 void
72 repo_empty(Repo *repo, int reuseids)
73 {
74   Pool *pool = repo->pool;
75   Solvable *s;
76   int i;
77
78   pool_freewhatprovides(pool);
79   if (reuseids && repo->end == pool->nsolvables)
80     {
81       /* it's ok to reuse the ids. As this is the last repo, we can
82          just shrink the solvable array */
83       for (i = repo->end - 1, s = pool->solvables + i; i >= repo->start; i--, s--)
84         if (s->repo != repo)
85           break;
86       pool_free_solvable_block(pool, i + 1, repo->end - (i + 1), reuseids);
87     }
88   /* zero out (i.e. free) solvables belonging to this repo */
89   for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
90     if (s->repo == repo)
91       memset(s, 0, sizeof(*s));
92   repo->nsolvables = 0;
93
94   /* free all data belonging to this repo */
95   repo->idarraydata = sat_free(repo->idarraydata);
96   repo->idarraysize = 0;
97   repo->lastoff = 0;
98   repo->rpmdbid = sat_free(repo->rpmdbid);
99   for (i = 0; i < repo->nrepodata; i++)
100     repodata_freedata(repo->repodata + i);
101   sat_free(repo->repodata);
102   repo->repodata = 0;
103   repo->nrepodata = 0;
104 }
105
106 /*
107  * remove repo from pool, delete solvables
108  *
109  */
110
111 void
112 repo_free(Repo *repo, int reuseids)
113 {
114   Pool *pool = repo->pool;
115   int i;
116
117   if (repo == pool->installed)
118     pool->installed = 0;
119   repo_empty(repo, reuseids);
120   for (i = 0; i < pool->nrepos; i++)    /* find repo in pool */
121     if (pool->repos[i] == repo)
122       break;
123   if (i == pool->nrepos)               /* repo not in pool, return */
124     return;
125   if (i < pool->nrepos - 1)
126     {
127       memmove(pool->repos + i, pool->repos + i + 1, (pool->nrepos - 1 - i) * sizeof(Repo *));
128       /* fix repo ids */
129       for (; i < pool->nrepos - 1; i++)
130         pool->repos[i]->repoid = i + 1;
131     }
132   pool->nrepos--;
133   repo_freedata(repo);
134 }
135
136 void
137 repo_freeallrepos(Pool *pool, int reuseids)
138 {
139   int i;
140
141   pool_freewhatprovides(pool);
142   for (i = 0; i < pool->nrepos; i++)
143     repo_freedata(pool->repos[i]);
144   pool->repos = sat_free(pool->repos);
145   pool->nrepos = 0;
146   /* the first two solvables don't belong to a repo */
147   pool_free_solvable_block(pool, 2, pool->nsolvables - 2, reuseids);
148 }
149
150 void repo_free_solvable_block(Repo *repo, Id start, int count, int reuseids)
151 {
152   Solvable *s;
153   Repodata *data;
154   int i;
155   if (start + count == repo->end)
156     repo->end -= count;
157   repo->nsolvables -= count;
158   for (s = repo->pool->solvables + start, i = count; i--; s++)
159     s->repo = 0;
160   pool_free_solvable_block(repo->pool, start, count, reuseids);
161   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
162     if (data->end > repo->end)
163       repodata_shrink(data, repo->end);
164 }
165
166
167 /* repository sidedata is solvable data allocated on demand.
168  * It is used for data that is normally not present
169  * in the solvable like the rpmdbid.
170  * The solvable allocation funcions need to make sure that
171  * the sidedata gets extended if new solvables get added.
172  */
173
174 #define REPO_SIDEDATA_BLOCK 63
175
176 void *
177 repo_sidedata_create(Repo *repo, size_t size)
178 {
179   return sat_calloc_block(repo->end - repo->start, size, REPO_SIDEDATA_BLOCK);
180 }
181
182 void *
183 repo_sidedata_extend(Repo *repo, void *b, size_t size, Id p, int count)
184 {
185   int n = repo->end - repo->start;
186   if (p < repo->start)
187     {
188       int d = repo->start - p;
189       b = sat_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
190       memmove((char *)b + d * size, b, n * size);
191       memset(b, 0, d * size);
192       n += d;
193     }
194   if (p + count > repo->end)
195     {
196       int d = p + count - repo->end;
197       b = sat_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
198       memset((char *)b + n * size, 0, d * size);
199     }
200   return b;
201 }
202
203 /*
204  * add Id to idarraydata used to store dependencies
205  * olddeps: old array offset to extend
206  * returns new array offset
207  */
208
209 Offset
210 repo_addid(Repo *repo, Offset olddeps, Id id)
211 {
212   Id *idarray;
213   int idarraysize;
214   int i;
215
216   idarray = repo->idarraydata;
217   idarraysize = repo->idarraysize;
218
219   if (!idarray)                        /* alloc idarray if not done yet */
220     {
221       idarraysize = 1;
222       idarray = sat_extend_resize(0, 1, sizeof(Id), IDARRAY_BLOCK);
223       idarray[0] = 0;
224       repo->lastoff = 0;
225     }
226
227   if (!olddeps)                         /* no deps yet */
228     {
229       olddeps = idarraysize;
230       idarray = sat_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
231     }
232   else if (olddeps == repo->lastoff)    /* extend at end */
233     idarraysize--;
234   else                                  /* can't extend, copy old */
235     {
236       i = olddeps;
237       olddeps = idarraysize;
238       for (; idarray[i]; i++)
239         {
240           idarray = sat_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
241           idarray[idarraysize++] = idarray[i];
242         }
243       idarray = sat_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
244     }
245
246   idarray[idarraysize++] = id;          /* insert Id into array */
247   idarray = sat_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
248   idarray[idarraysize++] = 0;           /* ensure NULL termination */
249
250   repo->idarraydata = idarray;
251   repo->idarraysize = idarraysize;
252   repo->lastoff = olddeps;
253
254   return olddeps;
255 }
256
257
258 /*
259  * add dependency (as Id) to repo, also unifies dependencies
260  * olddeps = offset into idarraydata
261  * marker= 0 for normal dep
262  * marker > 0 add dep after marker
263  * marker < 0 add dep after -marker
264  * returns new start of dependency array
265  */
266 Offset
267 repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker)
268 {
269   Id oid, *oidp, *markerp;
270   int before;
271
272   if (!olddeps)
273     {
274       if (marker > 0)
275         olddeps = repo_addid(repo, olddeps, marker);
276       return repo_addid(repo, olddeps, id);
277     }
278
279   if (!marker)
280     {
281       for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != ID_NULL; oidp++)
282         {
283           if (oid == id)
284             return olddeps;
285         }
286       return repo_addid(repo, olddeps, id);
287     }
288
289   before = 0;
290   markerp = 0;
291   if (marker < 0)
292     {
293       before = 1;
294       marker = -marker;
295     }
296   for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != ID_NULL; oidp++)
297     {
298       if (oid == marker)
299         markerp = oidp;
300       else if (oid == id)
301         break;
302     }
303
304   if (oid)
305     {
306       if (markerp || before)
307         return olddeps;
308       /* we found it, but in the wrong half */
309       markerp = oidp++;
310       for (; (oid = *oidp) != ID_NULL; oidp++)
311         if (oid == marker)
312           break;
313       if (!oid)
314         {
315           /* no marker in array yet */
316           oidp--;
317           if (markerp < oidp)
318             memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
319           *oidp = marker;
320           return repo_addid(repo, olddeps, id);
321         }
322       while (oidp[1])
323         oidp++;
324       memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
325       *oidp = id;
326       return olddeps;
327     }
328   /* id not yet in array */
329   if (!before && !markerp)
330     olddeps = repo_addid(repo, olddeps, marker);
331   else if (before && markerp)
332     {
333       *markerp++ = id;
334       id = *--oidp;
335       if (markerp < oidp)
336         memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
337       *markerp = marker;
338     }
339   return repo_addid(repo, olddeps, id);
340 }
341
342
343 /*
344  * reserve Ids
345  * make space for 'num' more dependencies
346  * returns new start of dependency array
347  *
348  * reserved ids will always begin at offset idarraysize
349  */
350
351 Offset
352 repo_reserve_ids(Repo *repo, Offset olddeps, int num)
353 {
354   num++;        /* room for trailing ID_NULL */
355
356   if (!repo->idarraysize)              /* ensure buffer space */
357     {
358       repo->idarraysize = 1;
359       repo->idarraydata = sat_extend_resize(0, 1 + num, sizeof(Id), IDARRAY_BLOCK);
360       repo->idarraydata[0] = 0;
361       repo->lastoff = 1;
362       return 1;
363     }
364
365   if (olddeps && olddeps != repo->lastoff)   /* if not appending */
366     {
367       /* can't insert into idarray, this would invalidate all 'larger' offsets
368        * so create new space at end and move existing deps there.
369        * Leaving 'hole' at old position.
370        */
371
372       Id *idstart, *idend;
373       int count;
374
375       for (idstart = idend = repo->idarraydata + olddeps; *idend++; )   /* find end */
376         ;
377       count = idend - idstart - 1 + num;               /* new size */
378
379       repo->idarraydata = sat_extend(repo->idarraydata, repo->idarraysize, count, sizeof(Id), IDARRAY_BLOCK);
380       /* move old deps to end */
381       olddeps = repo->lastoff = repo->idarraysize;
382       memcpy(repo->idarraydata + olddeps, idstart, count - num);
383       repo->idarraysize = olddeps + count - num;
384
385       return olddeps;
386     }
387
388   if (olddeps)                         /* appending */
389     repo->idarraysize--;
390
391   /* make room*/
392   repo->idarraydata = sat_extend(repo->idarraydata, repo->idarraysize, num, sizeof(Id), IDARRAY_BLOCK);
393
394   /* appending or new */
395   repo->lastoff = olddeps ? olddeps : repo->idarraysize;
396
397   return repo->lastoff;
398 }
399
400
401
402 Offset
403 repo_fix_supplements(Repo *repo, Offset provides, Offset supplements, Offset freshens)
404 {
405   Pool *pool = repo->pool;
406   Id id, idp, idl;
407   char buf[1024], *p, *dep;
408   int i, l;
409
410   if (provides)
411     {
412       for (i = provides; repo->idarraydata[i]; i++)
413         {
414           id = repo->idarraydata[i];
415           if (ISRELDEP(id))
416             continue;
417           dep = (char *)id2str(pool, id);
418           if (!strncmp(dep, "locale(", 7) && strlen(dep) < sizeof(buf) - 2)
419             {
420               idp = 0;
421               strcpy(buf + 2, dep);
422               dep = buf + 2 + 7;
423               if ((p = strchr(dep, ':')) != 0 && p != dep)
424                 {
425                   *p++ = 0;
426                   idp = str2id(pool, dep, 1);
427                   dep = p;
428                 }
429               id = 0;
430               while ((p = strchr(dep, ';')) != 0)
431                 {
432                   if (p == dep)
433                     {
434                       dep = p + 1;
435                       continue;
436                     }
437                   *p++ = 0;
438 #if 0
439                   strncpy(dep - 9, "language:", 9);
440                   idl = str2id(pool, dep - 9, 1);
441 #else
442                   idl = str2id(pool, dep, 1);
443                   idl = rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
444 #endif
445                   if (id)
446                     id = rel2id(pool, id, idl, REL_OR, 1);
447                   else
448                     id = idl;
449                   dep = p;
450                 }
451               if (dep[0] && dep[1])
452                 {
453                   for (p = dep; *p && *p != ')'; p++)
454                     ;
455                   *p = 0;
456 #if 0
457                   strncpy(dep - 9, "language:", 9);
458                   idl = str2id(pool, dep - 9, 1);
459 #else
460                   idl = str2id(pool, dep, 1);
461                   idl = rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
462 #endif
463                   if (id)
464                     id = rel2id(pool, id, idl, REL_OR, 1);
465                   else
466                     id = idl;
467                 }
468               if (idp)
469                 id = rel2id(pool, idp, id, REL_AND, 1);
470               if (id)
471                 supplements = repo_addid_dep(repo, supplements, id, 0);
472             }
473           else if ((p = strchr(dep, ':')) != 0 && p != dep && p[1] == '/' && strlen(dep) < sizeof(buf))
474             {
475               strcpy(buf, dep);
476               p = buf + (p - dep);
477               *p++ = 0;
478               idp = str2id(pool, buf, 1);
479               /* strip trailing slashes */
480               l = strlen(p);
481               while (l > 1 && p[l - 1] == '/')
482                 p[--l] = 0;
483               id = str2id(pool, p, 1);
484               id = rel2id(pool, idp, id, REL_WITH, 1);
485               id = rel2id(pool, NAMESPACE_SPLITPROVIDES, id, REL_NAMESPACE, 1);
486               supplements = repo_addid_dep(repo, supplements, id, 0);
487             }
488         }
489     }
490   if (supplements)
491     {
492       for (i = supplements; repo->idarraydata[i]; i++)
493         {
494           id = repo->idarraydata[i];
495           if (ISRELDEP(id))
496             continue;
497           dep = (char *)id2str(pool, id);
498           if (!strncmp(dep, "system:modalias(", 16))
499             dep += 7;
500           if (!strncmp(dep, "modalias(", 9) && dep[9] && dep[10] && strlen(dep) < sizeof(buf))
501             {
502               strcpy(buf, dep);
503               p = strchr(buf + 9, ':');
504               if (p && p != buf + 9 && strchr(p + 1, ':'))
505                 {
506                   *p++ = 0;
507                   idp = str2id(pool, buf + 9, 1);
508                   p[strlen(p) - 1] = 0;
509                   id = str2id(pool, p, 1);
510                   id = rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
511                   id = rel2id(pool, idp, id, REL_AND, 1);
512                 }
513               else
514                 {
515                   p = buf + 9;
516                   p[strlen(p) - 1] = 0;
517                   id = str2id(pool, p, 1);
518                   id = rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
519                 }
520               if (id)
521                 repo->idarraydata[i] = id;
522             }
523           else if (!strncmp(dep, "packageand(", 11) && strlen(dep) < sizeof(buf))
524             {
525               strcpy(buf, dep);
526               id = 0;
527               dep = buf + 11;
528               while ((p = strchr(dep, ':')) != 0)
529                 {
530                   if (p == dep)
531                     {
532                       dep = p + 1;
533                       continue;
534                     }
535                   *p++ = 0;
536                   idp = str2id(pool, dep, 1);
537                   if (id)
538                     id = rel2id(pool, id, idp, REL_AND, 1);
539                   else
540                     id = idp;
541                   dep = p;
542                 }
543               if (dep[0] && dep[1])
544                 {
545                   dep[strlen(dep) - 1] = 0;
546                   idp = str2id(pool, dep, 1);
547                   if (id)
548                     id = rel2id(pool, id, idp, REL_AND, 1);
549                   else
550                     id = idp;
551                 }
552               if (id)
553                 repo->idarraydata[i] = id;
554             }
555           else if (!strncmp(dep, "filesystem(", 11) && strlen(dep) < sizeof(buf))
556             {
557               strcpy(buf, dep + 11);
558               if ((p = strrchr(buf, ')')) != 0)
559                 *p = 0;
560               id = str2id(pool, buf, 1);
561               id = rel2id(pool, NAMESPACE_FILESYSTEM, id, REL_NAMESPACE, 1);
562               repo->idarraydata[i] = id;
563             }
564         }
565     }
566   if (freshens && repo->idarraydata[freshens])
567     {
568       Id idsupp = 0, idfresh = 0;
569       if (!supplements)
570         return freshens;
571       for (i = supplements; repo->idarraydata[i]; i++)
572         {
573           if (!idsupp)
574             idsupp = repo->idarraydata[i];
575           else
576             idsupp = rel2id(pool, idsupp, repo->idarraydata[i], REL_OR, 1);
577         }
578       for (i = freshens; repo->idarraydata[i]; i++)
579         {
580           if (!idfresh)
581             idfresh = repo->idarraydata[i];
582           else
583             idfresh = rel2id(pool, idfresh, repo->idarraydata[i], REL_OR, 1);
584         }
585       if (!idsupp)
586         idsupp = idfresh;
587       else
588         idsupp = rel2id(pool, idsupp, idfresh, REL_AND, 1);
589       supplements = repo_addid_dep(repo, 0, idsupp, 0);
590     }
591   return supplements;
592 }
593
594 Offset
595 repo_fix_conflicts(Repo *repo, Offset conflicts)
596 {
597   char buf[1024], *p, *dep;
598   Pool *pool = repo->pool;
599   Id id;
600   int i;
601
602   if (!conflicts)
603     return conflicts;
604   for (i = conflicts; repo->idarraydata[i]; i++)
605     {
606       id = repo->idarraydata[i];
607       if (ISRELDEP(id))
608         continue;
609       dep = (char *)id2str(pool, id);
610       if (!strncmp(dep, "otherproviders(", 15) && strlen(dep) < sizeof(buf) - 2)
611         {
612           strcpy(buf, dep + 15);
613           if ((p = strchr(buf, ')')) != 0)
614             *p = 0;
615           id = str2id(pool, buf, 1);
616           id = rel2id(pool, NAMESPACE_OTHERPROVIDERS, id, REL_NAMESPACE, 1);
617           repo->idarraydata[i] = id;
618         }
619     }
620   return conflicts;
621 }
622
623 struct matchdata
624 {
625   Pool *pool;
626   int flags;
627   Datamatcher matcher;
628   int stop;
629   int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv);
630   void *callback_data;
631 };
632
633 int
634 repo_matchvalue(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
635 {
636   struct matchdata *md = cbdata;
637
638   if (md->matcher.match)
639     {
640       if (!repodata_stringify(md->pool, data, key, kv, md->flags))
641         return 0;
642       if (!datamatcher_match(&md->matcher, kv->str))
643         return 0;
644     }
645   md->stop = md->callback(md->callback_data, s, data, key, kv);
646   return md->stop;
647 }
648
649
650 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
651   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
652   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
653   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
654   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
655   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
656   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
657   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
658   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
659   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
660   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
661   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
662   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
663   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
664 };
665
666 static void
667 domatch_idarray(Solvable *s, Id keyname, struct matchdata *md, Id *ida)
668 {
669   KeyValue kv;
670   kv.entry = 0;
671   kv.parent = 0;
672   for (; *ida && !md->stop; ida++)
673     {
674       kv.id = *ida;
675       kv.eof = ida[1] ? 0 : 1;
676       repo_matchvalue(md, s, 0, solvablekeys + (keyname - SOLVABLE_NAME), &kv);
677       kv.entry++;
678     }
679 }
680
681 static void
682 repo_search_md(Repo *repo, Id p, Id keyname, struct matchdata *md)
683 {
684   KeyValue kv;
685   Pool *pool = repo->pool;
686   Repodata *data;
687   int i, j, flags;
688   Solvable *s;
689
690   kv.parent = 0;
691   md->stop = 0;
692   if (!p)
693     {
694       for (p = repo->start, s = repo->pool->solvables + p; p < repo->end; p++, s++)
695         {
696           if (s->repo == repo)
697             repo_search_md(repo, p, keyname, md);
698           if (md->stop > SEARCH_NEXT_SOLVABLE)
699             break;
700         }
701       return;
702     }
703   else if (p < 0)
704     /* The callback only supports solvables, so we can't iterate over the
705        extra things.  */
706     return;
707   flags = md->flags;
708   if (!(flags & SEARCH_NO_STORAGE_SOLVABLE))
709     {
710       s = pool->solvables + p;
711       switch(keyname)
712         {
713           case 0:
714           case SOLVABLE_NAME:
715             if (s->name)
716               {
717                 kv.id = s->name;
718                 repo_matchvalue(md, s, 0, solvablekeys + 0, &kv);
719               }
720             if (keyname || md->stop > SEARCH_NEXT_KEY)
721               return;
722           case SOLVABLE_ARCH:
723             if (s->arch)
724               {
725                 kv.id = s->arch;
726                 repo_matchvalue(md, s, 0, solvablekeys + 1, &kv);
727               }
728             if (keyname || md->stop > SEARCH_NEXT_KEY)
729               return;
730           case SOLVABLE_EVR:
731             if (s->evr)
732               {
733                 kv.id = s->evr;
734                 repo_matchvalue(md, s, 0, solvablekeys + 2, &kv);
735               }
736             if (keyname || md->stop > SEARCH_NEXT_KEY)
737               return;
738           case SOLVABLE_VENDOR:
739             if (s->vendor)
740               {
741                 kv.id = s->vendor;
742                 repo_matchvalue(md, s, 0, solvablekeys + 3, &kv);
743               }
744             if (keyname || md->stop > SEARCH_NEXT_KEY)
745               return;
746           case SOLVABLE_PROVIDES:
747             if (s->provides)
748               domatch_idarray(s, SOLVABLE_PROVIDES, md, repo->idarraydata + s->provides);
749             if (keyname || md->stop > SEARCH_NEXT_KEY)
750               return;
751           case SOLVABLE_OBSOLETES:
752             if (s->obsoletes)
753               domatch_idarray(s, SOLVABLE_OBSOLETES, md, repo->idarraydata + s->obsoletes);
754             if (keyname || md->stop > SEARCH_NEXT_KEY)
755               return;
756           case SOLVABLE_CONFLICTS:
757             if (s->conflicts)
758               domatch_idarray(s, SOLVABLE_CONFLICTS, md, repo->idarraydata + s->conflicts);
759             if (keyname || md->stop > SEARCH_NEXT_KEY)
760               return;
761           case SOLVABLE_REQUIRES:
762             if (s->requires)
763               domatch_idarray(s, SOLVABLE_REQUIRES, md, repo->idarraydata + s->requires);
764             if (keyname || md->stop > SEARCH_NEXT_KEY)
765               return;
766           case SOLVABLE_RECOMMENDS:
767             if (s->recommends)
768               domatch_idarray(s, SOLVABLE_RECOMMENDS, md, repo->idarraydata + s->recommends);
769             if (keyname || md->stop > SEARCH_NEXT_KEY)
770               return;
771           case SOLVABLE_SUPPLEMENTS:
772             if (s->supplements)
773               domatch_idarray(s, SOLVABLE_SUPPLEMENTS, md, repo->idarraydata + s->supplements);
774             if (keyname || md->stop > SEARCH_NEXT_KEY)
775               return;
776           case SOLVABLE_SUGGESTS:
777             if (s->suggests)
778               domatch_idarray(s, SOLVABLE_SUGGESTS, md, repo->idarraydata + s->suggests);
779             if (keyname || md->stop > SEARCH_NEXT_KEY)
780               return;
781           case SOLVABLE_ENHANCES:
782             if (s->enhances)
783               domatch_idarray(s, SOLVABLE_ENHANCES, md, repo->idarraydata + s->enhances);
784             if (keyname || md->stop > SEARCH_NEXT_KEY)
785               return;
786           case RPM_RPMDBID:
787             if (repo->rpmdbid)
788               {
789                 kv.num = repo->rpmdbid[p - repo->start];
790                 repo_matchvalue(md, s, 0, solvablekeys + (RPM_RPMDBID - SOLVABLE_NAME), &kv);
791               }
792             if (keyname || md->stop > SEARCH_NEXT_KEY)
793               return;
794             break;
795           default:
796             break;
797         }
798     }
799
800   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
801     {
802       if (p < data->start || p >= data->end)
803         continue;
804       if (keyname && !repodata_precheck_keyname(data, keyname))
805         continue;
806       if (keyname == SOLVABLE_FILELIST && !(md->flags & SEARCH_COMPLETE_FILELIST))
807         {
808           /* do not search filelist extensions */
809           if (data->state != REPODATA_AVAILABLE)
810             continue;
811           for (j = 1; j < data->nkeys; j++)
812             if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
813               break;
814           if (j == data->nkeys)
815             continue;
816         }
817       if (data->state == REPODATA_STUB)
818         {
819           if (keyname)
820             {
821               for (j = 1; j < data->nkeys; j++)
822                 if (keyname == data->keys[j].name)
823                   break;
824               if (j == data->nkeys)
825                 continue;
826             }
827           /* load it */
828           if (data->loadcallback)
829             data->loadcallback(data);
830           else
831             data->state = REPODATA_ERROR;
832         }
833       if (data->state == REPODATA_ERROR)
834         continue;
835       repodata_search(data, p, keyname, md->flags, repo_matchvalue, md);
836       if (md->stop > SEARCH_NEXT_KEY)
837         break;
838     }
839 }
840
841 void
842 repo_search(Repo *repo, Id p, Id keyname, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
843 {
844   struct matchdata md;
845
846   if (repo->disabled && !(flags & SEARCH_DISABLED_REPOS))
847     return;
848   memset(&md, 0, sizeof(md));
849   md.pool = repo->pool;
850   md.flags = flags;
851   md.callback = callback;
852   md.callback_data = cbdata;
853   if (match)
854     datamatcher_init(&md.matcher, match, flags);
855   repo_search_md(repo, p, keyname, &md);
856   if (match)
857     datamatcher_free(&md.matcher);
858 }
859
860 const char *
861 repo_lookup_str(Repo *repo, Id entry, Id keyname)
862 {
863   Pool *pool = repo->pool;
864   Repodata *data;
865   int i;
866   const char *str;
867
868   if (entry >= 0)
869     {
870       switch (keyname)
871         {
872         case SOLVABLE_NAME:
873           return id2str(pool, pool->solvables[entry].name);
874         case SOLVABLE_ARCH:
875           return id2str(pool, pool->solvables[entry].arch);
876         case SOLVABLE_EVR:
877           return id2str(pool, pool->solvables[entry].evr);
878         case SOLVABLE_VENDOR:
879           return id2str(pool, pool->solvables[entry].vendor);
880         }
881     }
882   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
883     {
884       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
885         continue;
886       if (!repodata_precheck_keyname(data, keyname))
887         continue;
888       str = repodata_lookup_str(data, entry, keyname);
889       if (str)
890         return str;
891       if (repodata_lookup_type(data, entry, keyname))
892         return 0;
893     }
894   return 0;
895 }
896
897
898 unsigned int
899 repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned int notfound)
900 {
901   Repodata *data;
902   int i;
903   unsigned int value;
904
905   if (entry >= 0)
906     {
907       if (keyname == RPM_RPMDBID)
908         {
909           if (repo->rpmdbid && entry >= repo->start && entry < repo->end)
910             return repo->rpmdbid[entry - repo->start];
911           return notfound;
912         }
913     }
914   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
915     {
916       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
917         continue;
918       if (!repodata_precheck_keyname(data, keyname))
919         continue;
920       if (repodata_lookup_num(data, entry, keyname, &value))
921         return value;
922       if (repodata_lookup_type(data, entry, keyname))
923         return notfound;
924     }
925   return notfound;
926 }
927
928 Id
929 repo_lookup_id(Repo *repo, Id entry, Id keyname)
930 {
931   Repodata *data;
932   int i;
933   Id id;
934
935   if (entry >= 0)
936     {
937       switch (keyname)
938         {
939         case SOLVABLE_NAME:
940           return repo->pool->solvables[entry].name;
941         case SOLVABLE_ARCH:
942           return repo->pool->solvables[entry].arch;
943         case SOLVABLE_EVR:
944           return repo->pool->solvables[entry].evr;
945         case SOLVABLE_VENDOR:
946           return repo->pool->solvables[entry].vendor;
947         }
948     }
949   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
950     {
951       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
952         continue;
953       if (!repodata_precheck_keyname(data, keyname))
954         continue;
955       id = repodata_lookup_id(data, entry, keyname);
956       if (id)
957         return data->localpool ? repodata_globalize_id(data, id, 1) : id;
958       if (repodata_lookup_type(data, entry, keyname))
959         return 0;
960     }
961   return 0;
962 }
963
964 static int
965 lookup_idarray_solvable(Repo *repo, Offset off, Queue *q)
966 {
967   Id *p;
968
969   queue_empty(q);
970   if (off)
971     for (p = repo->idarraydata + off; *p; p++)
972       queue_push(q, *p);
973   return 1;
974 }
975
976 int
977 repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q)
978 {
979   Repodata *data;
980   int i;
981   if (entry >= 0)
982     {
983       switch (keyname)
984         {
985         case SOLVABLE_PROVIDES:
986           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].provides, q);
987         case SOLVABLE_OBSOLETES:
988           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].obsoletes, q);
989         case SOLVABLE_CONFLICTS:
990           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].conflicts, q);
991         case SOLVABLE_REQUIRES:
992           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].requires, q);
993         case SOLVABLE_RECOMMENDS:
994           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].recommends, q);
995         case SOLVABLE_SUGGESTS:
996           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].suggests, q);
997         case SOLVABLE_SUPPLEMENTS:
998           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].supplements, q);
999         case SOLVABLE_ENHANCES:
1000           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].enhances, q);
1001         }
1002     }
1003   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
1004     {
1005       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1006         continue;
1007       if (!repodata_precheck_keyname(data, keyname))
1008         continue;
1009       if (repodata_lookup_idarray(data, entry, keyname, q))
1010         {
1011           if (data->localpool)
1012             {
1013               for (i = 0; i < q->count; i++)
1014                 q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
1015             }
1016           return 1;
1017         }
1018       if (repodata_lookup_type(data, entry, keyname))
1019         break;
1020     }
1021   queue_empty(q);
1022   return 0;
1023 }
1024
1025 const unsigned char *
1026 repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
1027 {
1028   Repodata *data;
1029   int i;
1030   const unsigned char *chk;
1031
1032   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
1033     {
1034       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1035         continue;
1036       if (!repodata_precheck_keyname(data, keyname))
1037         continue;
1038       chk = repodata_lookup_bin_checksum(data, entry, keyname, typep);
1039       if (chk)
1040         return chk;
1041       if (repodata_lookup_type(data, entry, keyname))
1042         return 0;
1043     }
1044   *typep = 0;
1045   return 0;
1046 }
1047
1048 const char *
1049 repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
1050 {
1051   const unsigned char *chk = repo_lookup_bin_checksum(repo, entry, keyname, typep);
1052   return chk ? pool_bin2hex(repo->pool, chk, sat_chksum_len(*typep)) : 0;
1053 }
1054
1055 int
1056 repo_lookup_void(Repo *repo, Id entry, Id keyname)
1057 {
1058   Repodata *data;
1059   int i;
1060   Id type;
1061
1062   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
1063     {
1064       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1065         continue;
1066       if (!repodata_precheck_keyname(data, keyname))
1067         continue;
1068       type = repodata_lookup_type(data, entry, keyname);
1069       if (type)
1070         return type == REPOKEY_TYPE_VOID;
1071     }
1072   return 0;
1073 }
1074
1075 Id
1076 repo_lookup_type(Repo *repo, Id entry, Id keyname)
1077 {
1078   Repodata *data;
1079   int i;
1080   Id type;
1081
1082   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
1083     {
1084       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1085         continue;
1086       if (!repodata_precheck_keyname(data, keyname))
1087         continue;
1088       type = repodata_lookup_type(data, entry, keyname);
1089       if (type)
1090         return type == REPOKEY_TYPE_DELETED ? 0 : type;
1091     }
1092   return 0;
1093 }
1094
1095 /***********************************************************************/
1096
1097 Repodata *
1098 repo_add_repodata(Repo *repo, int flags)
1099 {
1100   int i;
1101   if ((flags & REPO_USE_LOADING) != 0)
1102     {
1103       for (i = repo->nrepodata - 1; i >= 0; i--)
1104         if (repo->repodata[i].state == REPODATA_LOADING)
1105           {
1106             Repodata *data = repo->repodata + i;
1107             /* re-init */
1108             /* hack: we mis-use REPO_REUSE_REPODATA here */
1109             if (!(flags & REPO_REUSE_REPODATA))
1110               repodata_empty(data, (flags & REPO_LOCALPOOL) ? 1 : 0);
1111             return data;
1112           }
1113       return 0; /* must not create a new repodata! */
1114     }
1115   if ((flags & REPO_REUSE_REPODATA) != 0)
1116     {
1117       for (i = repo->nrepodata - 1; i >= 0; i--)
1118         if (repo->repodata[i].state != REPODATA_STUB)
1119           return repo->repodata + i;
1120     }
1121   return repodata_create(repo, (flags & REPO_LOCALPOOL) ? 1 : 0);
1122 }
1123
1124 Repodata *
1125 repo_last_repodata(Repo *repo)
1126 {
1127   int i;
1128   for (i = repo->nrepodata - 1; i >= 0; i--)
1129     if (repo->repodata[i].state != REPODATA_STUB)
1130       return repo->repodata + i;
1131   return repo_add_repodata(repo, 0);
1132 }
1133
1134 void
1135 repo_set_id(Repo *repo, Id p, Id keyname, Id id)
1136 {
1137   Repodata *data;
1138   if (p >= 0)
1139     {
1140       switch (keyname)
1141         {
1142         case SOLVABLE_NAME:
1143           repo->pool->solvables[p].name = id;
1144           return;
1145         case SOLVABLE_ARCH:
1146           repo->pool->solvables[p].arch = id;
1147           return;
1148         case SOLVABLE_EVR:
1149           repo->pool->solvables[p].evr = id;
1150           return;
1151         case SOLVABLE_VENDOR:
1152           repo->pool->solvables[p].vendor = id;
1153           return;
1154         }
1155     }
1156   data = repo_last_repodata(repo);
1157   if (data->localpool)
1158     id = repodata_localize_id(data, id, 1);
1159   repodata_set_id(data, p, keyname, id);
1160 }
1161
1162 void
1163 repo_set_num(Repo *repo, Id p, Id keyname, unsigned int num)
1164 {
1165   Repodata *data;
1166   if (p >= 0)
1167     {
1168       if (keyname == RPM_RPMDBID)
1169         {
1170           if (!repo->rpmdbid)
1171             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1172           repo->rpmdbid[p] = num;
1173           return;
1174         }
1175     }
1176   data = repo_last_repodata(repo);
1177   repodata_set_num(data, p, keyname, num);
1178 }
1179
1180 void
1181 repo_set_str(Repo *repo, Id p, Id keyname, const char *str)
1182 {
1183   Repodata *data;
1184   if (p >= 0)
1185     {
1186       switch (keyname)
1187         {
1188         case SOLVABLE_NAME:
1189         case SOLVABLE_ARCH:
1190         case SOLVABLE_EVR:
1191         case SOLVABLE_VENDOR:
1192           repo_set_id(repo, p, keyname, str2id(repo->pool, str, 1));
1193           return;
1194         }
1195     }
1196   data = repo_last_repodata(repo);
1197   repodata_set_str(data, p, keyname, str);
1198 }
1199
1200 void
1201 repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str)
1202 {
1203   Repodata *data;
1204   if (p >= 0)
1205     {
1206       switch (keyname)
1207         {
1208         case SOLVABLE_NAME:
1209         case SOLVABLE_ARCH:
1210         case SOLVABLE_EVR:
1211         case SOLVABLE_VENDOR:
1212           repo_set_id(repo, p, keyname, str2id(repo->pool, str, 1));
1213           return;
1214         }
1215     }
1216   data = repo_last_repodata(repo);
1217   repodata_set_poolstr(data, p, keyname, str);
1218 }
1219
1220 void
1221 repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str)
1222 {
1223   Repodata *data = repo_last_repodata(repo);
1224   repodata_add_poolstr_array(data, p, keyname, str);
1225 }
1226
1227 void
1228 repo_internalize(Repo *repo)
1229 {
1230   int i;
1231   Repodata *data;
1232
1233   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
1234     if (data->attrs || data->xattrs)
1235       repodata_internalize(data);
1236 }
1237
1238 void
1239 repo_disable_paging(Repo *repo)
1240 {
1241   int i;
1242   Repodata *data;
1243
1244   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
1245     repodata_disable_paging(data);
1246 }
1247 // EOF
1248 /*
1249 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1250 */