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