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