Imported Upstream version 0.7.20
[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   if (!pool->nrepos)
46     {
47       pool->nrepos = 1; /* start with repoid 1 */
48       pool->repos = (Repo **)solv_calloc(2, sizeof(Repo *));
49     }
50   else
51     pool->repos = (Repo **)solv_realloc2(pool->repos, pool->nrepos + 1, sizeof(Repo *));
52   pool->repos[pool->nrepos] = repo;
53   pool->urepos++;
54   repo->repoid = pool->nrepos++;
55   repo->name = name ? solv_strdup(name) : 0;
56   repo->pool = pool;
57   repo->start = pool->nsolvables;
58   repo->end = pool->nsolvables;
59   repo->nsolvables = 0;
60   return repo;
61 }
62
63 void
64 repo_freedata(Repo *repo)
65 {
66   int i;
67   for (i = 1; i < repo->nrepodata; i++)
68     repodata_freedata(repo->repodata + i);
69   solv_free(repo->repodata);
70   solv_free(repo->idarraydata);
71   solv_free(repo->rpmdbid);
72   solv_free(repo->lastidhash);
73   solv_free((char *)repo->name);
74   solv_free(repo);
75 }
76
77 /* delete all solvables and repodata blocks from this repo */
78
79 void
80 repo_empty(Repo *repo, int reuseids)
81 {
82   Pool *pool = repo->pool;
83   Solvable *s;
84   int i;
85
86   pool_freewhatprovides(pool);
87   if (reuseids && repo->end == pool->nsolvables)
88     {
89       /* it's ok to reuse the ids. As this is the last repo, we can
90          just shrink the solvable array */
91       for (i = repo->end - 1, s = pool->solvables + i; i >= repo->start; i--, s--)
92         if (s->repo != repo)
93           break;
94       pool_free_solvable_block(pool, i + 1, repo->end - (i + 1), reuseids);
95       repo->end = i + 1;
96     }
97   /* zero out (i.e. free) solvables belonging to this repo */
98   for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
99     if (s->repo == repo)
100       memset(s, 0, sizeof(*s));
101   repo->end = repo->start;
102   repo->nsolvables = 0;
103
104   /* free all data belonging to this repo */
105   repo->idarraydata = solv_free(repo->idarraydata);
106   repo->idarraysize = 0;
107   repo->lastoff = 0;
108   repo->rpmdbid = solv_free(repo->rpmdbid);
109   for (i = 1; i < repo->nrepodata; i++)
110     repodata_freedata(repo->repodata + i);
111   solv_free(repo->repodata);
112   repo->repodata = 0;
113   repo->nrepodata = 0;
114 }
115
116 /*
117  * remove repo from pool, delete solvables
118  *
119  */
120
121 void
122 repo_free(Repo *repo, int reuseids)
123 {
124   Pool *pool = repo->pool;
125   int i;
126
127   if (repo == pool->installed)
128     pool->installed = 0;
129   repo_empty(repo, reuseids);
130   for (i = 1; i < pool->nrepos; i++)    /* find repo in pool */
131     if (pool->repos[i] == repo)
132       break;
133   if (i == pool->nrepos)               /* repo not in pool, return */
134     return;
135   if (i == pool->nrepos - 1 && reuseids)
136     pool->nrepos--;
137   else
138     pool->repos[i] = 0;
139   pool->urepos--;
140   repo_freedata(repo);
141 }
142
143 Id
144 repo_add_solvable(Repo *repo)
145 {
146   Id p = pool_add_solvable(repo->pool);
147   if (!repo->start || repo->start == repo->end)
148     repo->start = repo->end = p;
149   /* warning: sidedata must be extended before adapting start/end */
150   if (repo->rpmdbid)
151     repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, 1);
152   if (p < repo->start)
153     repo->start = p;
154   if (p + 1 > repo->end)
155     repo->end = p + 1;
156   repo->nsolvables++;
157   repo->pool->solvables[p].repo = repo;
158   return p;
159 }
160
161 Id
162 repo_add_solvable_block(Repo *repo, int count)
163 {
164   Id p;
165   Solvable *s;
166   if (!count)
167     return 0;
168   p = pool_add_solvable_block(repo->pool, count);
169   if (!repo->start || repo->start == repo->end)
170     repo->start = repo->end = p;
171   /* warning: sidedata must be extended before adapting start/end */
172   if (repo->rpmdbid)
173     repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, count);
174   if (p < repo->start)
175     repo->start = p;
176   if (p + count > repo->end)
177     repo->end = p + count;
178   repo->nsolvables += count;
179   for (s = repo->pool->solvables + p; count--; s++)
180     s->repo = repo;
181   return p;
182 }
183
184 void
185 repo_free_solvable(Repo *repo, Id p, int reuseids)
186 {
187   repo_free_solvable_block(repo, p, 1, reuseids);
188 }
189
190 void
191 repo_free_solvable_block(Repo *repo, Id start, int count, int reuseids)
192 {
193   Solvable *s;
194   Repodata *data;
195   int i;
196   if (start + count == repo->end)
197     repo->end -= count;
198   repo->nsolvables -= count;
199   for (s = repo->pool->solvables + start, i = count; i--; s++)
200     s->repo = 0;
201   pool_free_solvable_block(repo->pool, start, count, reuseids);
202   FOR_REPODATAS(repo, i, data)
203     {
204       int dstart, dend;
205       if (data->end > repo->end)
206         repodata_shrink(data, repo->end);
207       dstart = data->start > start ? data->start : start;
208       dend = data->end < start + count ? data->end : start + count;
209       if (dstart < dend)
210         {
211           if (data->attrs)
212             {
213               int j;
214               for (j = dstart; j < dend; j++)   
215                 data->attrs[j - data->start] = solv_free(data->attrs[j - data->start]);
216               if (data->lasthandle >= dstart && data->lasthandle < dend)
217                 data->lasthandle = 0;
218             }
219           if (data->incoreoffset)
220             memset(data->incoreoffset + (dstart - data->start), 0, (dend - dstart) * sizeof(Id));
221         }
222     }
223 }
224
225 /* specialized version of repo_add_solvable_block that inserts the new solvable
226  * block before the indicated repo, which gets relocated.
227  * used in repo_add_rpmdb
228  */
229 Id
230 repo_add_solvable_block_before(Repo *repo, int count, Repo *beforerepo)
231 {
232   Pool *pool = repo->pool;
233   Id p;
234   Solvable *s;
235   Repodata *data;
236   int i;
237
238   if (!count || !beforerepo || beforerepo->end != pool->nsolvables || beforerepo->start == beforerepo->end)
239     return repo_add_solvable_block(repo, count);
240   p = beforerepo->start;
241   /* make sure all solvables belong to beforerepo */
242   for (i = p, s = pool->solvables + i; i < beforerepo->end; i++, s++)
243     if (s->repo && s->repo != beforerepo)
244       return repo_add_solvable_block(repo, count);
245   /* now move beforerepo to back */
246   pool_add_solvable_block(pool, count); /* must return beforerepo->end! */
247   memmove(pool->solvables + p + count, pool->solvables + p, (beforerepo->end - p) * sizeof(Solvable));
248   memset(pool->solvables + p, 0, sizeof(Solvable) * count);
249   /* adapt repodata */
250   FOR_REPODATAS(beforerepo, i, data)
251     {
252       if (data->start < p)
253         continue;
254       data->start += count;
255       data->end += count;
256     }
257   beforerepo->start += count;
258   beforerepo->end += count;
259   /* we now have count free solvables at id p */
260   /* warning: sidedata must be extended before adapting start/end */
261   if (repo->rpmdbid)
262     repo->rpmdbid = (Id *)repo_sidedata_extend(repo, repo->rpmdbid, sizeof(Id), p, count);
263   if (p < repo->start)
264     repo->start = p;
265   if (p + count > repo->end)
266     repo->end = p + count;
267   repo->nsolvables += count;
268   for (s = pool->solvables + p; count--; s++)
269     s->repo = repo;
270   return p;
271 }
272
273
274 /* repository sidedata is solvable data allocated on demand.
275  * It is used for data that is normally not present
276  * in the solvable like the rpmdbid.
277  * The solvable allocation funcions need to make sure that
278  * the sidedata gets extended if new solvables get added.
279  */
280
281 #define REPO_SIDEDATA_BLOCK 63
282
283 void *
284 repo_sidedata_create(Repo *repo, size_t size)
285 {
286   return solv_calloc_block(repo->end - repo->start, size, REPO_SIDEDATA_BLOCK);
287 }
288
289 void *
290 repo_sidedata_extend(Repo *repo, void *b, size_t size, Id p, int count)
291 {
292   int n = repo->end - repo->start;
293   if (p < repo->start)
294     {
295       int d = repo->start - p;
296       b = solv_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
297       memmove((char *)b + d * size, b, n * size);
298       memset(b, 0, d * size);
299       n += d;
300     }
301   if (p + count > repo->end)
302     {
303       int d = p + count - repo->end;
304       b = solv_extend(b, n, d, size, REPO_SIDEDATA_BLOCK);
305       memset((char *)b + n * size, 0, d * size);
306     }
307   return b;
308 }
309
310 /*
311  * add Id to idarraydata used to store dependencies
312  * olddeps: old array offset to extend
313  * returns new array offset
314  */
315
316 Offset
317 repo_addid(Repo *repo, Offset olddeps, Id id)
318 {
319   Id *idarray;
320   int idarraysize;
321   int i;
322
323   idarray = repo->idarraydata;
324   idarraysize = repo->idarraysize;
325
326   if (!idarray)                        /* alloc idarray if not done yet */
327     {
328       idarraysize = 1;
329       idarray = solv_extend_resize(0, 1, sizeof(Id), IDARRAY_BLOCK);
330       idarray[0] = 0;
331       repo->lastoff = 0;
332     }
333
334   if (!olddeps)                         /* no deps yet */
335     {
336       olddeps = idarraysize;
337       idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
338     }
339   else if (olddeps == repo->lastoff)    /* extend at end */
340     idarraysize--;
341   else                                  /* can't extend, copy old */
342     {
343       i = olddeps;
344       olddeps = idarraysize;
345       for (; idarray[i]; i++)
346         {
347           idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
348           idarray[idarraysize++] = idarray[i];
349         }
350       idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
351     }
352
353   idarray[idarraysize++] = id;          /* insert Id into array */
354   idarray = solv_extend(idarray, idarraysize, 1, sizeof(Id), IDARRAY_BLOCK);
355   idarray[idarraysize++] = 0;           /* ensure NULL termination */
356
357   repo->idarraydata = idarray;
358   repo->idarraysize = idarraysize;
359   repo->lastoff = olddeps;
360
361   return olddeps;
362 }
363
364 #define REPO_ADDID_DEP_HASHTHRES        64
365 #define REPO_ADDID_DEP_HASHMIN          128
366
367 /*
368  * Optimization for packages with an excessive amount of provides/requires:
369  * if the number of deps exceed a threshold, we build a hash of the already
370  * seen ids.
371  */
372 static Offset
373 repo_addid_dep_hash(Repo *repo, Offset olddeps, Id id, Id marker, int size)
374 {
375   Id oid, *oidp;
376   int before;
377   Hashval h, hh;
378   Id hid;
379
380   before = 0;
381   if (marker)
382     {
383       if (marker < 0)
384         {
385           marker = -marker;
386           before = 1;
387         }
388       if (marker == id)
389         marker = 0;
390     }
391
392   /* maintain hash and lastmarkerpos */
393   if (repo->lastidhash_idarraysize != repo->idarraysize || (Hashval)size * 2 > repo->lastidhash_mask || repo->lastmarker != marker)
394     {
395       repo->lastmarkerpos = 0;
396       if ((Hashval)size * 2 > repo->lastidhash_mask)
397         {
398           repo->lastidhash_mask = mkmask(size < REPO_ADDID_DEP_HASHMIN ? REPO_ADDID_DEP_HASHMIN : size);
399           repo->lastidhash = solv_realloc2(repo->lastidhash, repo->lastidhash_mask + 1, sizeof(Id));
400         }
401       memset(repo->lastidhash, 0, (repo->lastidhash_mask + 1) * sizeof(Id));
402       for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
403         {
404           h = oid & repo->lastidhash_mask;
405           hh = HASHCHAIN_START;
406           while (repo->lastidhash[h] != 0)
407             h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
408           repo->lastidhash[h] = oid;
409           if (marker && oid == marker)
410             repo->lastmarkerpos = oidp - repo->idarraydata;
411         }
412       repo->lastmarker = marker;
413       repo->lastidhash_idarraysize = repo->idarraysize;
414     }
415
416   /* check the hash! */
417   h = id & repo->lastidhash_mask;
418   hh = HASHCHAIN_START;
419   while ((hid = repo->lastidhash[h]) != 0 && hid != id)
420     h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
421   /* put new element in hash */
422   if (!hid)
423     repo->lastidhash[h] = id;
424   else if (marker == SOLVABLE_FILEMARKER && (!before || !repo->lastmarkerpos))
425     return olddeps;
426   if (marker && !before && !repo->lastmarkerpos)
427     {
428       /* we have to add the marker first */
429       repo->lastmarkerpos = repo->idarraysize - 1;
430       olddeps = repo_addid(repo, olddeps, marker);
431       /* now put marker in hash */
432       h = marker & repo->lastidhash_mask;
433       hh = HASHCHAIN_START;
434       while (repo->lastidhash[h] != 0)
435         h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
436       repo->lastidhash[h] = marker;
437       repo->lastidhash_idarraysize = repo->idarraysize;
438     }
439   if (!hid)
440     {
441       /* new entry, insert in correct position */
442       if (marker && before && repo->lastmarkerpos)
443         {
444           /* need to add it before the marker */
445           olddeps = repo_addid(repo, olddeps, id);      /* dummy to make room */
446           memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (repo->idarraysize - repo->lastmarkerpos - 2) * sizeof(Id));
447           repo->idarraydata[repo->lastmarkerpos++] = id;
448         }
449       else
450         {
451           /* just append it to the end */
452           olddeps = repo_addid(repo, olddeps, id);
453         }
454       repo->lastidhash_idarraysize = repo->idarraysize;
455       return olddeps;
456     }
457   /* we already have it in the hash */
458   if (!marker)
459     return olddeps;
460   if (marker == SOLVABLE_FILEMARKER)
461     {
462       /* check if it is in the wrong half */
463       /* (we already made sure that "before" and "lastmarkerpos" are set, see above) */
464       for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
465         if (oid == id)
466           break;
467       if (!oid)
468         return olddeps;
469       /* yes, wrong half. copy it over */
470       memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (oidp - (repo->idarraydata + repo->lastmarkerpos)) * sizeof(Id));
471       repo->idarraydata[repo->lastmarkerpos++] = id;
472       return olddeps;
473     }
474   if (before)
475     return olddeps;
476   /* check if it is in the correct half */
477   for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
478     if (oid == id)
479       return olddeps;
480   /* nope, copy it over */
481   for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
482     if (oid == id)
483       break;
484   if (!oid)
485     return olddeps;     /* should not happen */
486   memmove(oidp, oidp + 1, (repo->idarraydata + repo->idarraysize - oidp - 2) * sizeof(Id));
487   repo->idarraydata[repo->idarraysize - 2] = id;
488   repo->lastmarkerpos--;        /* marker has been moved */
489   return olddeps;
490 }
491
492 /*
493  * add dependency (as Id) to repo, also unifies dependencies
494  * olddeps = offset into idarraydata
495  * marker= 0 for normal dep
496  * marker > 0 add dep after marker
497  * marker < 0 add dep before -marker
498  * returns new start of dependency array
499  */
500 Offset
501 repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker)
502 {
503   Id oid, *oidp, *markerp;
504   int before;
505
506   if (!olddeps)
507     {
508       if (marker > 0)
509         olddeps = repo_addid(repo, olddeps, marker);
510       return repo_addid(repo, olddeps, id);
511     }
512
513   /* check if we should use the hash optimization */
514   if (olddeps == repo->lastoff)
515     {
516       int size = repo->idarraysize - 1 - repo->lastoff;
517       if (size >= REPO_ADDID_DEP_HASHTHRES)
518         return repo_addid_dep_hash(repo, olddeps, id, marker, size);
519     }
520
521   before = 0;
522   if (marker)
523     {
524       if (marker < 0)
525         {
526           marker = -marker;
527           before = 1;
528         }
529       if (marker == id)
530         marker = 0;
531     }
532
533   if (!marker)
534     {
535       for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
536         if (oid == id)
537           return olddeps;
538       return repo_addid(repo, olddeps, id);
539     }
540
541   markerp = 0;
542   for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
543     {
544       if (oid == marker)
545         markerp = oidp;
546       else if (oid == id)
547         break;
548     }
549
550   if (oid)
551     {
552       if (marker == SOLVABLE_FILEMARKER)
553         {
554           if (!markerp || !before)
555             return olddeps;
556           /* we found it, but in the second half */
557           memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
558           *markerp = id;
559           return olddeps;
560         }
561       if (markerp || before)
562         return olddeps;
563       /* we found it, but in the first half */
564       markerp = oidp++;
565       for (; (oid = *oidp) != 0; oidp++)
566         if (oid == marker)
567           break;
568       if (!oid)
569         {
570           /* no marker in array yet */
571           oidp--;
572           if (markerp < oidp)
573             memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
574           *oidp = marker;
575           return repo_addid(repo, olddeps, id);
576         }
577       while (oidp[1])
578         oidp++;
579       memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
580       *oidp = id;
581       return olddeps;
582     }
583   /* id not yet in array */
584   if (!before && !markerp)
585     olddeps = repo_addid(repo, olddeps, marker);
586   else if (before && markerp)
587     {
588       *markerp++ = id;
589       id = *--oidp;
590       if (markerp < oidp)
591         memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
592       *markerp = marker;
593     }
594   return repo_addid(repo, olddeps, id);
595 }
596
597 /* return standard marker for the keyname dependency.
598  * 1: return positive marker, -1: return negative marker
599  */
600 Id
601 solv_depmarker(Id keyname, Id marker)
602 {
603   if (marker != 1 && marker != -1)
604     return marker;
605   if (keyname == SOLVABLE_PROVIDES)
606     return marker < 0 ? -SOLVABLE_FILEMARKER : SOLVABLE_FILEMARKER;
607   if (keyname == SOLVABLE_REQUIRES)
608     return marker < 0 ? -SOLVABLE_PREREQMARKER : SOLVABLE_PREREQMARKER;
609   return 0;
610 }
611
612 /*
613  * reserve Ids
614  * make space for 'num' more dependencies
615  * returns new start of dependency array
616  *
617  * reserved ids will always begin at offset idarraysize
618  */
619 Offset
620 repo_reserve_ids(Repo *repo, Offset olddeps, int num)
621 {
622   num++;        /* room for trailing ID_NULL */
623
624   if (!repo->idarraysize)              /* ensure buffer space */
625     {
626       repo->idarraysize = 1;
627       repo->idarraydata = solv_extend_resize(0, 1 + num, sizeof(Id), IDARRAY_BLOCK);
628       repo->idarraydata[0] = 0;
629       repo->lastoff = 1;
630       return 1;
631     }
632
633   if (olddeps && olddeps != repo->lastoff)   /* if not appending */
634     {
635       /* can't insert into idarray, this would invalidate all 'larger' offsets
636        * so create new space at end and move existing deps there.
637        * Leaving 'hole' at old position.
638        */
639
640       Id *idstart, *idend;
641       int count;
642
643       for (idstart = idend = repo->idarraydata + olddeps; *idend++; )   /* find end */
644         ;
645       count = idend - idstart - 1 + num;               /* new size */
646
647       repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, count, sizeof(Id), IDARRAY_BLOCK);
648       /* move old deps to end */
649       olddeps = repo->lastoff = repo->idarraysize;
650       memcpy(repo->idarraydata + olddeps, idstart, count - num);
651       repo->idarraysize = olddeps + count - num;
652
653       return olddeps;
654     }
655
656   if (olddeps)                         /* appending */
657     repo->idarraysize--;
658
659   /* make room*/
660   repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, num, sizeof(Id), IDARRAY_BLOCK);
661
662   /* appending or new */
663   repo->lastoff = olddeps ? olddeps : repo->idarraysize;
664
665   return repo->lastoff;
666 }
667
668
669 /***********************************************************************/
670
671 struct matchdata
672 {
673   Pool *pool;
674   int flags;
675   Datamatcher matcher;
676   int stop;
677   Id *keyskip;
678   int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv);
679   void *callback_data;
680 };
681
682 static int
683 repo_matchvalue(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
684 {
685   struct matchdata *md = cbdata;
686
687   if (md->matcher.match)
688     {
689       const char *str;
690       if (key->name == SOLVABLE_FILELIST && key->type == REPOKEY_TYPE_DIRSTRARRAY && (md->matcher.flags & SEARCH_FILES) != 0)
691         if (!datamatcher_checkbasename(&md->matcher, kv->str))
692           return 0;
693       if (!(str = repodata_stringify(md->pool, data, key, kv, md->flags)))
694         return 0;
695       if (!datamatcher_match(&md->matcher, str))
696         return 0;
697     }
698   else
699     {
700       /* stringify filelist if requested */
701       if (key->name == SOLVABLE_FILELIST && key->type == REPOKEY_TYPE_DIRSTRARRAY && (md->matcher.flags & SEARCH_FILES) != 0)
702         repodata_stringify(md->pool, data, key, kv, md->flags);
703     }
704   md->stop = md->callback(md->callback_data, s, data, key, kv);
705   return md->stop;
706 }
707
708
709 /* list of all keys we store in the solvable */
710 /* also used in the dataiterator code in repodata.c */
711 Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
712   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
713   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
714   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
715   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
716   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
717   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
718   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
719   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
720   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
721   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
722   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
723   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
724   { RPM_RPMDBID,          REPOKEY_TYPE_NUM, 0, KEY_STORAGE_SOLVABLE },
725 };
726
727 static void
728 domatch_idarray(Solvable *s, Id keyname, struct matchdata *md, Id *ida)
729 {
730   KeyValue kv;
731   kv.entry = 0;
732   kv.parent = 0;
733   for (; *ida && !md->stop; ida++)
734     {
735       kv.id = *ida;
736       kv.eof = ida[1] ? 0 : 1;
737       repo_matchvalue(md, s, 0, repo_solvablekeys + (keyname - SOLVABLE_NAME), &kv);
738       kv.entry++;
739     }
740 }
741
742 static Offset *
743 solvable_offsetptr(Solvable *s, Id keyname)
744 {
745   switch(keyname)
746   {
747   case SOLVABLE_PROVIDES:
748     return &s->provides;
749   case SOLVABLE_OBSOLETES:
750     return &s->obsoletes;
751   case SOLVABLE_CONFLICTS:
752     return &s->conflicts;
753   case SOLVABLE_REQUIRES:
754     return &s->requires;
755   case SOLVABLE_RECOMMENDS:
756     return &s->recommends;
757   case SOLVABLE_SUGGESTS:
758     return &s->suggests;
759   case SOLVABLE_SUPPLEMENTS:
760     return &s->supplements;
761   case SOLVABLE_ENHANCES:
762     return &s->enhances;
763   default:
764     return 0;
765   }
766 }
767
768 static void
769 repo_search_md(Repo *repo, Id p, Id keyname, struct matchdata *md)
770 {
771   KeyValue kv;
772   Pool *pool = repo->pool;
773   Repodata *data;
774   int i, flags;
775   Solvable *s;
776   Id *keyskip;
777
778   kv.parent = 0;
779   md->stop = 0;
780   if (!p)
781     {
782       for (p = repo->start, s = repo->pool->solvables + p; p < repo->end; p++, s++)
783         {
784           if (s->repo == repo)
785             repo_search_md(repo, p, keyname, md);
786           if (md->stop > SEARCH_NEXT_SOLVABLE)
787             break;
788         }
789       return;
790     }
791   if (p < 0 && p != SOLVID_META)
792     return;             /* SOLVID_POS not supported yet */
793   flags = md->flags;
794   if (p > 0 && !(flags & SEARCH_NO_STORAGE_SOLVABLE))
795     {
796       s = pool->solvables + p;
797       switch(keyname)
798         {
799           case 0:
800           case SOLVABLE_NAME:
801             if (s->name)
802               {
803                 kv.id = s->name;
804                 repo_matchvalue(md, s, 0, repo_solvablekeys + 0, &kv);
805               }
806             if (keyname || md->stop > SEARCH_NEXT_KEY)
807               return;
808           case SOLVABLE_ARCH:
809             if (s->arch)
810               {
811                 kv.id = s->arch;
812                 repo_matchvalue(md, s, 0, repo_solvablekeys + 1, &kv);
813               }
814             if (keyname || md->stop > SEARCH_NEXT_KEY)
815               return;
816           case SOLVABLE_EVR:
817             if (s->evr)
818               {
819                 kv.id = s->evr;
820                 repo_matchvalue(md, s, 0, repo_solvablekeys + 2, &kv);
821               }
822             if (keyname || md->stop > SEARCH_NEXT_KEY)
823               return;
824           case SOLVABLE_VENDOR:
825             if (s->vendor)
826               {
827                 kv.id = s->vendor;
828                 repo_matchvalue(md, s, 0, repo_solvablekeys + 3, &kv);
829               }
830             if (keyname || md->stop > SEARCH_NEXT_KEY)
831               return;
832           case SOLVABLE_PROVIDES:
833             if (s->provides)
834               domatch_idarray(s, SOLVABLE_PROVIDES, md, repo->idarraydata + s->provides);
835             if (keyname || md->stop > SEARCH_NEXT_KEY)
836               return;
837           case SOLVABLE_OBSOLETES:
838             if (s->obsoletes)
839               domatch_idarray(s, SOLVABLE_OBSOLETES, md, repo->idarraydata + s->obsoletes);
840             if (keyname || md->stop > SEARCH_NEXT_KEY)
841               return;
842           case SOLVABLE_CONFLICTS:
843             if (s->conflicts)
844               domatch_idarray(s, SOLVABLE_CONFLICTS, md, repo->idarraydata + s->conflicts);
845             if (keyname || md->stop > SEARCH_NEXT_KEY)
846               return;
847           case SOLVABLE_REQUIRES:
848             if (s->requires)
849               domatch_idarray(s, SOLVABLE_REQUIRES, md, repo->idarraydata + s->requires);
850             if (keyname || md->stop > SEARCH_NEXT_KEY)
851               return;
852           case SOLVABLE_RECOMMENDS:
853             if (s->recommends)
854               domatch_idarray(s, SOLVABLE_RECOMMENDS, md, repo->idarraydata + s->recommends);
855             if (keyname || md->stop > SEARCH_NEXT_KEY)
856               return;
857           case SOLVABLE_SUPPLEMENTS:
858             if (s->supplements)
859               domatch_idarray(s, SOLVABLE_SUPPLEMENTS, md, repo->idarraydata + s->supplements);
860             if (keyname || md->stop > SEARCH_NEXT_KEY)
861               return;
862           case SOLVABLE_SUGGESTS:
863             if (s->suggests)
864               domatch_idarray(s, SOLVABLE_SUGGESTS, md, repo->idarraydata + s->suggests);
865             if (keyname || md->stop > SEARCH_NEXT_KEY)
866               return;
867           case SOLVABLE_ENHANCES:
868             if (s->enhances)
869               domatch_idarray(s, SOLVABLE_ENHANCES, md, repo->idarraydata + s->enhances);
870             if (keyname || md->stop > SEARCH_NEXT_KEY)
871               return;
872           case RPM_RPMDBID:
873             if (repo->rpmdbid)
874               {
875                 kv.num = (unsigned int)repo->rpmdbid[p - repo->start];
876                 kv.num2 = 0;
877                 repo_matchvalue(md, s, 0, repo_solvablekeys + (RPM_RPMDBID - SOLVABLE_NAME), &kv);
878               }
879             if (keyname || md->stop > SEARCH_NEXT_KEY)
880               return;
881             break;
882           default:
883             break;
884         }
885     }
886
887   if (keyname)
888     {
889       if (keyname == SOLVABLE_FILELIST)
890         data = repo_lookup_filelist_repodata(repo, p, &md->matcher);
891       else
892         data = repo_lookup_repodata_opt(repo, p, keyname);
893       if (data)
894         repodata_search(data, p, keyname, md->flags, repo_matchvalue, md);
895       return;
896     }
897
898   keyskip = repo_create_keyskip(repo, p, &md->keyskip);
899   FOR_REPODATAS(repo, i, data)
900     {
901       if (p != SOLVID_META && (p < data->start || p >= data->end))
902         continue;
903       repodata_search_keyskip(data, p, keyname, md->flags, keyskip, repo_matchvalue, md);
904       if (md->stop > SEARCH_NEXT_KEY)
905         break;
906     }
907 }
908
909 void
910 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)
911 {
912   struct matchdata md;
913
914   if (repo->disabled && !(flags & SEARCH_DISABLED_REPOS))
915     return;
916   memset(&md, 0, sizeof(md));
917   md.pool = repo->pool;
918   md.flags = flags;
919   md.callback = callback;
920   md.callback_data = cbdata;
921   if (match)
922     datamatcher_init(&md.matcher, match, flags);
923   repo_search_md(repo, p, keyname, &md);
924   if (match)
925     datamatcher_free(&md.matcher);
926   solv_free(md.keyskip);
927 }
928
929 Repodata *
930 repo_lookup_repodata(Repo *repo, Id entry, Id keyname)
931 {
932   Repodata *data;
933   int rdid;
934   Id type;
935
936   if (entry == SOLVID_POS)
937     {
938       Pool *pool = repo->pool;
939       return pool->pos.repo == repo && pool->pos.repodataid ? pool->pos.repo->repodata + pool->pos.repodataid : 0;
940     }
941   for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--)
942     {
943       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
944         continue;
945       if (!repodata_precheck_keyname(data, keyname))
946         continue;
947       if ((type = repodata_lookup_type(data, entry, keyname)) != 0)
948         return type == REPOKEY_TYPE_DELETED ? 0 : data;
949     }
950   return 0;
951 }
952
953 /* like repo_lookup_repodata, but may return a repodata that contains no match instead of NULL */
954 Repodata *
955 repo_lookup_repodata_opt(Repo *repo, Id entry, Id keyname)
956 {
957   Repodata *data, *found = 0;
958   int rdid;
959   Id type;
960
961   if (entry == SOLVID_POS)
962     {
963       Pool *pool = repo->pool;
964       return pool->pos.repo == repo && pool->pos.repodataid ? pool->pos.repo->repodata + pool->pos.repodataid : 0;
965     }
966   for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--)
967     {
968       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
969         continue;
970       if (!repodata_precheck_keyname(data, keyname))
971         continue;
972       if (found && (type = repodata_lookup_type(found, entry, keyname)) != 0)
973         return type == REPOKEY_TYPE_DELETED ? 0 : found;
974       found = data;
975     }
976   return found;
977 }
978
979 Repodata *
980 repo_lookup_filelist_repodata(Repo *repo, Id entry, Datamatcher *matcher)
981 {
982   Repodata *data;
983   int haveextension;
984   int rdid;
985   Id type;
986
987   if (entry <= 0 || !matcher || !matcher->match || ((matcher->flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
988       && (matcher->flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB))
989     return repo_lookup_repodata_opt(repo, entry, SOLVABLE_FILELIST);    /* cannot use filtered filelist */
990
991   haveextension = 0;
992   for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--)
993     {    
994       if (entry < data->start || entry >= data->end)
995         continue;
996       if (data->filelisttype == REPODATA_FILELIST_FILTERED)
997         {
998           if (data->state != REPODATA_AVAILABLE)
999             {
1000               if (data->state != REPODATA_STUB)
1001                 continue;
1002               repodata_load(data);
1003               if (data->state != REPODATA_AVAILABLE || entry < data->start || entry >= data->end)
1004                 continue;
1005             }
1006           /* does this contain any data about the solvable we're looking for? */
1007           if (!data->incoreoffset[entry - data->start])
1008             continue;   /* no, ignore */
1009           if (haveextension && repodata_filelistfilter_matches(data, matcher->match))
1010             return data;
1011           break;        /* fall back to normal code */
1012         }
1013       if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
1014         continue;
1015       if (data->filelisttype == REPODATA_FILELIST_EXTENSION)
1016         {
1017           haveextension++;
1018           continue;
1019         }
1020       if ((type = repodata_lookup_type(data, entry, SOLVABLE_FILELIST)) != 0)
1021         {
1022           if (haveextension)
1023             break;              /* need to look in extension */
1024           return type == REPOKEY_TYPE_DELETED ? 0 : data;
1025         }
1026     }
1027   /* cannot use filtered filelist */
1028   return repo_lookup_repodata_opt(repo, entry, SOLVABLE_FILELIST);
1029 }
1030
1031
1032 /* the keyskip array has the following format:
1033  * 0: keyname area size
1034  * 1: repoid base
1035  * 2: repoid end
1036  * 3: entry for keyname 0
1037  * 4: entry for keyname 1
1038  * ...
1039  */
1040 Id *
1041 repo_create_keyskip(Repo *repo, Id entry, Id **oldkeyskip)
1042 {
1043   Repodata *data, *last = 0;
1044   Id *keyskip;
1045   int rdid, cnt = 0;
1046
1047   if (repo->nrepodata <= 2)
1048     return 0;   /* just one repodata, nothing to filter */
1049   keyskip = oldkeyskip ? *oldkeyskip : 0;
1050   if (keyskip)
1051     {
1052       if (keyskip[1] >= 0x10000000)
1053         keyskip = solv_free(keyskip);
1054       else
1055         keyskip[1] = keyskip[2];
1056     }
1057   FOR_REPODATAS(repo, rdid, data)
1058     {
1059       if (entry != SOLVID_META)
1060         {
1061           if (data->state != REPODATA_AVAILABLE && data->state != REPODATA_LOADING)
1062             {
1063               if (data->state != REPODATA_STUB)
1064                 continue;
1065               repodata_load(data);
1066               if (data->state != REPODATA_AVAILABLE)
1067                 continue;
1068             }
1069           if ((entry < data->start || entry >= data->end))
1070             continue;
1071           if (!data->incoreoffset[entry - data->start])
1072             continue;
1073         }
1074       if (last)
1075         keyskip = repodata_fill_keyskip(last, entry, keyskip);
1076       last = data;
1077       cnt++;
1078     }
1079   if (cnt <= 1)
1080     {
1081       if (oldkeyskip)
1082         *oldkeyskip = keyskip;
1083       return 0;
1084     }
1085   keyskip = repodata_fill_keyskip(last, entry, keyskip);
1086   if (keyskip)
1087     keyskip[2] = keyskip[1] + repo->nrepodata;
1088   if (oldkeyskip)
1089     *oldkeyskip = keyskip;
1090   return keyskip;
1091 }
1092
1093 const char *
1094 repo_lookup_str(Repo *repo, Id entry, Id keyname)
1095 {
1096   Repodata *data;
1097
1098   if (entry >= 0)
1099     {
1100       Pool *pool = repo->pool;
1101       switch (keyname)
1102         {
1103         case SOLVABLE_NAME:
1104           return pool_id2str(pool, pool->solvables[entry].name);
1105         case SOLVABLE_ARCH:
1106           return pool_id2str(pool, pool->solvables[entry].arch);
1107         case SOLVABLE_EVR:
1108           return pool_id2str(pool, pool->solvables[entry].evr);
1109         case SOLVABLE_VENDOR:
1110           return pool_id2str(pool, pool->solvables[entry].vendor);
1111         }
1112     }
1113   data = repo_lookup_repodata_opt(repo, entry, keyname);
1114   return data ? repodata_lookup_str(data, entry, keyname) : 0;
1115 }
1116
1117
1118 unsigned long long
1119 repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned long long notfound)
1120 {
1121   Repodata *data;
1122
1123   if (entry >= 0)
1124     {
1125       if (keyname == RPM_RPMDBID)
1126         {
1127           if (repo->rpmdbid && entry >= repo->start && entry < repo->end)
1128             return (unsigned int)repo->rpmdbid[entry - repo->start];
1129           return notfound;
1130         }
1131     }
1132   data = repo_lookup_repodata_opt(repo, entry, keyname);
1133   return data ? repodata_lookup_num(data, entry, keyname, notfound) : notfound;
1134 }
1135
1136 Id
1137 repo_lookup_id(Repo *repo, Id entry, Id keyname)
1138 {
1139   Repodata *data;
1140   Id id;
1141
1142   if (entry >= 0)
1143     {
1144       switch (keyname)
1145         {
1146         case SOLVABLE_NAME:
1147           return repo->pool->solvables[entry].name;
1148         case SOLVABLE_ARCH:
1149           return repo->pool->solvables[entry].arch;
1150         case SOLVABLE_EVR:
1151           return repo->pool->solvables[entry].evr;
1152         case SOLVABLE_VENDOR:
1153           return repo->pool->solvables[entry].vendor;
1154         }
1155     }
1156   data = repo_lookup_repodata_opt(repo, entry, keyname);
1157   if (data && (id = repodata_lookup_id(data, entry, keyname)) != 0)
1158     return data->localpool ? repodata_globalize_id(data, id, 1) : id;
1159   return 0;
1160 }
1161
1162 int
1163 repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q)
1164 {
1165   Repodata *data;
1166   int i;
1167   if (entry >= 0)
1168     {
1169       Offset *offp;
1170       switch (keyname)
1171         {
1172         case SOLVABLE_PROVIDES:
1173         case SOLVABLE_OBSOLETES:
1174         case SOLVABLE_CONFLICTS:
1175         case SOLVABLE_REQUIRES:
1176         case SOLVABLE_RECOMMENDS:
1177         case SOLVABLE_SUGGESTS:
1178         case SOLVABLE_SUPPLEMENTS:
1179         case SOLVABLE_ENHANCES:
1180           offp = solvable_offsetptr(repo->pool->solvables + entry, keyname);
1181           if (*offp)
1182             {
1183               Id *p;
1184               for (p = repo->idarraydata + *offp; *p; p++)
1185                 queue_push(q, *p);
1186             }
1187           return 1;
1188         }
1189     }
1190   data = repo_lookup_repodata_opt(repo, entry, keyname);
1191   if (data && repodata_lookup_idarray(data, entry, keyname, q))
1192     {
1193       if (data->localpool)
1194         {
1195           for (i = 0; i < q->count; i++)
1196             q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
1197         }
1198       return 1;
1199     }
1200   queue_empty(q);
1201   return 0;
1202 }
1203
1204 int
1205 repo_lookup_deparray(Repo *repo, Id entry, Id keyname, Queue *q, Id marker)
1206 {
1207   int r = repo_lookup_idarray(repo, entry, keyname, q);
1208   if (!r)
1209     return 0;
1210   if (marker == -1 || marker == 1)
1211     marker = solv_depmarker(keyname, marker);
1212   if (marker && q->count)
1213     {
1214       int i;
1215       if (marker < 0)
1216         {
1217           marker = -marker;
1218           for (i = 0; i < q->count; i++)
1219             if (q->elements[i] == marker)
1220               {
1221                 queue_truncate(q, i);
1222                 return r;
1223               }
1224         }
1225       else
1226         {
1227           for (i = 0; i < q->count; i++)
1228             if (q->elements[i] == marker)
1229               {
1230                 queue_deleten(q, 0, i + 1);
1231                 return r;
1232               }
1233           queue_empty(q);
1234         }
1235     }
1236   return r;
1237 }
1238
1239 const unsigned char *
1240 repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
1241 {
1242   const unsigned char *chk;
1243   Repodata *data = repo_lookup_repodata_opt(repo, entry, keyname);
1244   if (data && (chk = repodata_lookup_bin_checksum(data, entry, keyname, typep)) != 0)
1245     return chk;
1246   *typep = 0;
1247   return 0;
1248 }
1249
1250 const char *
1251 repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
1252 {
1253   const unsigned char *chk = repo_lookup_bin_checksum(repo, entry, keyname, typep);
1254   return chk ? pool_bin2hex(repo->pool, chk, solv_chksum_len(*typep)) : 0;
1255 }
1256
1257 int
1258 repo_lookup_void(Repo *repo, Id entry, Id keyname)
1259 {
1260   Repodata *data = repo_lookup_repodata_opt(repo, entry, keyname);
1261   if (data)
1262     return repodata_lookup_void(data, entry, keyname);
1263   return 0;
1264 }
1265
1266 Id
1267 repo_lookup_type(Repo *repo, Id entry, Id keyname)
1268 {
1269   Id type;
1270   Repodata *data;
1271   if (keyname >= SOLVABLE_NAME && keyname <= RPM_RPMDBID)
1272     return repo_solvablekeys[keyname - SOLVABLE_NAME].type;
1273   data = repo_lookup_repodata_opt(repo, entry, keyname);
1274   if (data && (type = repodata_lookup_type(data, entry, keyname)) != 0 && type != REPOKEY_TYPE_DELETED)
1275     return type;
1276   return 0;
1277 }
1278
1279 const void *
1280 repo_lookup_binary(Repo *repo, Id entry, Id keyname, int *lenp)
1281 {
1282   const void *bin;
1283   Repodata *data = repo_lookup_repodata_opt(repo, entry, keyname);
1284   if (data && (bin = repodata_lookup_binary(data, entry, keyname, lenp)) != 0)
1285     return bin;
1286   *lenp = 0;
1287   return 0;
1288 }
1289
1290 unsigned int
1291 repo_lookup_count(Repo *repo, Id entry, Id keyname)
1292 {
1293   Repodata *data;
1294   if (keyname >= SOLVABLE_NAME && keyname <= RPM_RPMDBID)
1295   if (entry >= 0 && keyname >= SOLVABLE_NAME && keyname <= RPM_RPMDBID)
1296     {
1297       Id *p;
1298       Offset *offp;
1299       unsigned int cnt;
1300       switch (keyname)
1301         {
1302         case SOLVABLE_PROVIDES:
1303         case SOLVABLE_OBSOLETES:
1304         case SOLVABLE_CONFLICTS:
1305         case SOLVABLE_REQUIRES:
1306         case SOLVABLE_RECOMMENDS:
1307         case SOLVABLE_SUGGESTS:
1308         case SOLVABLE_SUPPLEMENTS:
1309         case SOLVABLE_ENHANCES:
1310           offp = solvable_offsetptr(repo->pool->solvables + entry, keyname);
1311           for (cnt = 0, p = repo->idarraydata + *offp; *p; p++)
1312             cnt++;
1313           return cnt;
1314         }
1315       return 1;
1316     }
1317   data = repo_lookup_repodata_opt(repo, entry, keyname);
1318   return data ? repodata_lookup_count(data, entry, keyname) : 0;
1319 }
1320
1321 /***********************************************************************/
1322
1323 Repodata *
1324 repo_add_repodata(Repo *repo, int flags)
1325 {
1326   Repodata *data;
1327   int i;
1328   if ((flags & REPO_USE_LOADING) != 0)
1329     {
1330       for (i = repo->nrepodata - 1; i > 0; i--)
1331         if (repo->repodata[i].state == REPODATA_LOADING)
1332           {
1333             Repodata *data = repo->repodata + i;
1334             /* re-init */
1335             /* hack: we mis-use REPO_REUSE_REPODATA here */
1336             if (!(flags & REPO_REUSE_REPODATA))
1337               repodata_empty(data, (flags & REPO_LOCALPOOL) ? 1 : 0);
1338             return data;
1339           }
1340       return 0; /* must not create a new repodata! */
1341     }
1342   if ((flags & REPO_REUSE_REPODATA) != 0)
1343     {
1344       for (i = repo->nrepodata - 1; i > 0; i--)
1345         if (repo->repodata[i].state != REPODATA_STUB)
1346           return repo->repodata + i;
1347     }
1348   if (!repo->nrepodata)
1349     {
1350       repo->nrepodata = 2;      /* start with id 1 */
1351       repo->repodata = solv_calloc(repo->nrepodata, sizeof(*data));
1352     }
1353   else
1354     {
1355       repo->nrepodata++;
1356       repo->repodata = solv_realloc2(repo->repodata, repo->nrepodata, sizeof(*data));
1357     }
1358   data = repo->repodata + repo->nrepodata - 1;
1359   repodata_initdata(data, repo, (flags & REPO_LOCALPOOL) ? 1 : 0);
1360   return data;
1361 }
1362
1363 Repodata *
1364 repo_id2repodata(Repo *repo, Id id)
1365 {
1366   return id ? repo->repodata + id : 0;
1367 }
1368
1369 Repodata *
1370 repo_last_repodata(Repo *repo)
1371 {
1372   int i;
1373   for (i = repo->nrepodata - 1; i > 0; i--)
1374     if (repo->repodata[i].state != REPODATA_STUB)
1375       return repo->repodata + i;
1376   return repo_add_repodata(repo, 0);
1377 }
1378
1379 void
1380 repo_set_id(Repo *repo, Id p, Id keyname, Id id)
1381 {
1382   Repodata *data;
1383   if (p >= 0)
1384     {
1385       switch (keyname)
1386         {
1387         case SOLVABLE_NAME:
1388           repo->pool->solvables[p].name = id;
1389           return;
1390         case SOLVABLE_ARCH:
1391           repo->pool->solvables[p].arch = id;
1392           return;
1393         case SOLVABLE_EVR:
1394           repo->pool->solvables[p].evr = id;
1395           return;
1396         case SOLVABLE_VENDOR:
1397           repo->pool->solvables[p].vendor = id;
1398           return;
1399         }
1400     }
1401   data = repo_last_repodata(repo);
1402   if (data->localpool)
1403     id = repodata_localize_id(data, id, 1);
1404   repodata_set_id(data, p, keyname, id);
1405 }
1406
1407 void
1408 repo_set_num(Repo *repo, Id p, Id keyname, unsigned long long num)
1409 {
1410   Repodata *data;
1411   if (p >= 0)
1412     {
1413       if (keyname == RPM_RPMDBID)
1414         {
1415           if (!repo->rpmdbid)
1416             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1417           repo->rpmdbid[p - repo->start] = (Id)num;
1418           return;
1419         }
1420     }
1421   data = repo_last_repodata(repo);
1422   repodata_set_num(data, p, keyname, num);
1423 }
1424
1425 void
1426 repo_set_str(Repo *repo, Id p, Id keyname, const char *str)
1427 {
1428   Repodata *data;
1429   if (p >= 0)
1430     {
1431       switch (keyname)
1432         {
1433         case SOLVABLE_NAME:
1434         case SOLVABLE_ARCH:
1435         case SOLVABLE_EVR:
1436         case SOLVABLE_VENDOR:
1437           repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
1438           return;
1439         }
1440     }
1441   data = repo_last_repodata(repo);
1442   repodata_set_str(data, p, keyname, str);
1443 }
1444
1445 void
1446 repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str)
1447 {
1448   Repodata *data;
1449   if (p >= 0)
1450     {
1451       switch (keyname)
1452         {
1453         case SOLVABLE_NAME:
1454         case SOLVABLE_ARCH:
1455         case SOLVABLE_EVR:
1456         case SOLVABLE_VENDOR:
1457           repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
1458           return;
1459         }
1460     }
1461   data = repo_last_repodata(repo);
1462   repodata_set_poolstr(data, p, keyname, str);
1463 }
1464
1465 void
1466 repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str)
1467 {
1468   Repodata *data = repo_last_repodata(repo);
1469   repodata_add_poolstr_array(data, p, keyname, str);
1470 }
1471
1472 void
1473 repo_add_deparray(Repo *repo, Id p, Id keyname, Id dep, Id marker)
1474 {
1475   Repodata *data;
1476   if (marker == -1 || marker == 1)
1477     marker = solv_depmarker(keyname, marker);
1478   if (p >= 0)
1479     {
1480       Offset *offp;
1481       switch (keyname)
1482         {
1483         case SOLVABLE_PROVIDES:
1484         case SOLVABLE_OBSOLETES:
1485         case SOLVABLE_CONFLICTS:
1486         case SOLVABLE_REQUIRES:
1487         case SOLVABLE_RECOMMENDS:
1488         case SOLVABLE_SUGGESTS:
1489         case SOLVABLE_SUPPLEMENTS:
1490         case SOLVABLE_ENHANCES:
1491           offp = solvable_offsetptr(repo->pool->solvables + p, keyname);
1492           *offp = repo_addid_dep(repo, *offp, dep, marker);
1493           return;
1494         }
1495     }
1496   data = repo_last_repodata(repo);
1497   repodata_add_idarray(data, p, keyname, dep);
1498 }
1499
1500 void
1501 repo_add_idarray(Repo *repo, Id p, Id keyname, Id id)
1502 {
1503   repo_add_deparray(repo, p, keyname, id, 0);
1504 }
1505
1506 void
1507 repo_set_deparray(Repo *repo, Id p, Id keyname, Queue *q, Id marker)
1508 {
1509   Repodata *data;
1510   if (marker == -1 || marker == 1)
1511     marker = solv_depmarker(keyname, marker);
1512   if (marker)
1513     {
1514       /* complex case, splice old and new arrays */
1515       int i;
1516       Queue q2;
1517       queue_init(&q2);
1518       repo_lookup_deparray(repo, p, keyname, &q2, -marker);
1519       if (marker > 0)
1520         {
1521           if (q->count)
1522             {
1523               queue_push(&q2, marker);
1524               for (i = 0; i < q->count; i++)
1525                 queue_push(&q2, q->elements[i]);
1526             }
1527         }
1528       else
1529         {
1530           if (q2.count)
1531             queue_insert(&q2, 0, -marker);
1532           queue_insertn(&q2, 0, q->count, q->elements);
1533         }
1534       repo_set_deparray(repo, p, keyname, &q2, 0);
1535       queue_free(&q2);
1536       return;
1537     }
1538   if (p >= 0)
1539     {
1540       Offset off, *offp;
1541       int i;
1542       switch (keyname)
1543         {
1544         case SOLVABLE_PROVIDES:
1545         case SOLVABLE_OBSOLETES:
1546         case SOLVABLE_CONFLICTS:
1547         case SOLVABLE_REQUIRES:
1548         case SOLVABLE_RECOMMENDS:
1549         case SOLVABLE_SUGGESTS:
1550         case SOLVABLE_SUPPLEMENTS:
1551         case SOLVABLE_ENHANCES:
1552           off = 0;
1553           for (i = 0; i < q->count; i++)
1554             off = repo_addid_dep(repo, off, q->elements[i], 0);
1555           offp = solvable_offsetptr(repo->pool->solvables + p, keyname);
1556           *offp = off;
1557           return;
1558         }
1559     }
1560   data = repo_last_repodata(repo);
1561   repodata_set_idarray(data, p, keyname, q);
1562 }
1563
1564 void
1565 repo_set_idarray(Repo *repo, Id p, Id keyname, Queue *q)
1566 {
1567   repo_set_deparray(repo, p, keyname, q, 0);
1568 }
1569
1570 void
1571 repo_unset(Repo *repo, Id p, Id keyname)
1572 {
1573   Repodata *data;
1574   if (p >= 0)
1575     {
1576       Solvable *s = repo->pool->solvables + p;
1577       switch (keyname)
1578         {
1579         case SOLVABLE_NAME:
1580           s->name = 0;
1581           return;
1582         case SOLVABLE_ARCH:
1583           s->arch = 0;
1584           return;
1585         case SOLVABLE_EVR:
1586           s->evr = 0;
1587           return;
1588         case SOLVABLE_VENDOR:
1589           s->vendor = 0;
1590           return;
1591         case RPM_RPMDBID:
1592           if (repo->rpmdbid)
1593             repo->rpmdbid[p - repo->start] = 0;
1594           return;
1595         case SOLVABLE_PROVIDES:
1596           s->provides = 0;
1597           return;
1598         case SOLVABLE_OBSOLETES:
1599           s->obsoletes = 0;
1600           return;
1601         case SOLVABLE_CONFLICTS:
1602           s->conflicts = 0;
1603           return;
1604         case SOLVABLE_REQUIRES:
1605           s->requires = 0;
1606           return;
1607         case SOLVABLE_RECOMMENDS:
1608           s->recommends = 0;
1609           return;
1610         case SOLVABLE_SUGGESTS:
1611           s->suggests = 0;
1612           return;
1613         case SOLVABLE_SUPPLEMENTS:
1614           s->supplements = 0;
1615         case SOLVABLE_ENHANCES:
1616           s->enhances = 0;
1617           return;
1618         default:
1619           break;
1620         }
1621     }
1622   data = repo_last_repodata(repo);
1623   repodata_unset(data, p, keyname);
1624 }
1625
1626 void
1627 repo_internalize(Repo *repo)
1628 {
1629   int i;
1630   Repodata *data;
1631
1632   FOR_REPODATAS(repo, i, data)
1633     if (data->attrs || data->xattrs)
1634       repodata_internalize(data);
1635 }
1636
1637 void
1638 repo_disable_paging(Repo *repo)
1639 {
1640   int i;
1641   Repodata *data;
1642
1643   FOR_REPODATAS(repo, i, data)
1644     repodata_disable_paging(data);
1645 }
1646