update markerpos if element needed to be moved
[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   else if (marker == SOLVABLE_FILEMARKER)
362     return olddeps;
363   if (marker && !before && !repo->lastmarkerpos)
364     {
365       /* we have to add the marker first */
366       repo->lastmarkerpos = repo->idarraysize - 1;
367       olddeps = repo_addid(repo, olddeps, marker);
368       /* now put marker in hash */
369       h = marker & repo->lastidhash_mask;
370       hh = HASHCHAIN_START;
371       while (repo->lastidhash[h] != 0)
372         h = HASHCHAIN_NEXT(h, hh, repo->lastidhash_mask);
373       repo->lastidhash[h] = marker;
374       repo->lastidhash_idarraysize = repo->idarraysize;
375     }
376   if (!hid)
377     {
378       /* new entry, insert in correct position */
379       if (marker && before && repo->lastmarkerpos)
380         {
381           /* need to add it before the marker */
382           olddeps = repo_addid(repo, olddeps, id);      /* dummy to make room */
383           memmove(repo->idarraydata + repo->lastmarkerpos + 1, repo->idarraydata + repo->lastmarkerpos, (repo->idarraysize - repo->lastmarkerpos - 2) * sizeof(Id));
384           repo->idarraydata[repo->lastmarkerpos++] = id;
385         }
386       else
387         {
388           /* just append it to the end */
389           olddeps = repo_addid(repo, olddeps, id);
390         }
391       repo->lastidhash_idarraysize = repo->idarraysize;
392       return olddeps;
393     }
394   /* we already have it in the hash */
395   if (!marker || before)
396     return olddeps;
397   /* check if it is in the correct half */
398   for (oidp = repo->idarraydata + repo->lastmarkerpos + 1; (oid = *oidp) != 0; oidp++)
399     if (oid == id)
400       return olddeps;
401   /* nope, copy it over */
402   for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
403     if (oid == id)
404       break;
405   if (!oid)
406     return olddeps;     /* should not happen */
407   memmove(oidp, oidp + 1, (repo->idarraydata + repo->idarraysize - oidp - 2) * sizeof(Id));
408   repo->idarraydata[repo->idarraysize - 2] = id;
409   repo->lastmarkerpos--;        /* marker has been moved */
410   return olddeps;
411 }
412
413 /*
414  * add dependency (as Id) to repo, also unifies dependencies
415  * olddeps = offset into idarraydata
416  * marker= 0 for normal dep
417  * marker > 0 add dep after marker
418  * marker < 0 add dep before -marker
419  * returns new start of dependency array
420  */
421 Offset
422 repo_addid_dep(Repo *repo, Offset olddeps, Id id, Id marker)
423 {
424   Id oid, *oidp, *markerp;
425   int before;
426
427   if (!olddeps)
428     {
429       if (marker > 0)
430         olddeps = repo_addid(repo, olddeps, marker);
431       return repo_addid(repo, olddeps, id);
432     }
433
434   /* check if we should use the hash optimization */
435   if (olddeps == repo->lastoff)
436     {
437       int size = repo->idarraysize - 1 - repo->lastoff;
438       if (size >= REPO_ADDID_DEP_HASHTHRES)
439         return repo_addid_dep_hash(repo, olddeps, id, marker, size);
440     }
441
442   before = 0;
443   if (marker)
444     {
445       if (marker < 0)
446         {
447           marker = -marker;
448           before = 1;
449         }
450       if (marker == id)
451         marker = 0;
452     }
453
454   if (!marker)
455     {
456       for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
457         if (oid == id)
458           return olddeps;
459       return repo_addid(repo, olddeps, id);
460     }
461
462   markerp = 0;
463   for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != 0; oidp++)
464     {
465       if (oid == marker)
466         markerp = oidp;
467       else if (oid == id)
468         break;
469     }
470
471   if (oid)
472     {
473       if (markerp || before || marker == SOLVABLE_FILEMARKER)
474         return olddeps;
475       /* we found it, but in the first half */
476       markerp = oidp++;
477       for (; (oid = *oidp) != 0; oidp++)
478         if (oid == marker)
479           break;
480       if (!oid)
481         {
482           /* no marker in array yet */
483           oidp--;
484           if (markerp < oidp)
485             memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
486           *oidp = marker;
487           return repo_addid(repo, olddeps, id);
488         }
489       while (oidp[1])
490         oidp++;
491       memmove(markerp, markerp + 1, (oidp - markerp) * sizeof(Id));
492       *oidp = id;
493       return olddeps;
494     }
495   /* id not yet in array */
496   if (!before && !markerp)
497     olddeps = repo_addid(repo, olddeps, marker);
498   else if (before && markerp)
499     {
500       *markerp++ = id;
501       id = *--oidp;
502       if (markerp < oidp)
503         memmove(markerp + 1, markerp, (oidp - markerp) * sizeof(Id));
504       *markerp = marker;
505     }
506   return repo_addid(repo, olddeps, id);
507 }
508
509
510 /*
511  * reserve Ids
512  * make space for 'num' more dependencies
513  * returns new start of dependency array
514  *
515  * reserved ids will always begin at offset idarraysize
516  */
517
518 Offset
519 repo_reserve_ids(Repo *repo, Offset olddeps, int num)
520 {
521   num++;        /* room for trailing ID_NULL */
522
523   if (!repo->idarraysize)              /* ensure buffer space */
524     {
525       repo->idarraysize = 1;
526       repo->idarraydata = solv_extend_resize(0, 1 + num, sizeof(Id), IDARRAY_BLOCK);
527       repo->idarraydata[0] = 0;
528       repo->lastoff = 1;
529       return 1;
530     }
531
532   if (olddeps && olddeps != repo->lastoff)   /* if not appending */
533     {
534       /* can't insert into idarray, this would invalidate all 'larger' offsets
535        * so create new space at end and move existing deps there.
536        * Leaving 'hole' at old position.
537        */
538
539       Id *idstart, *idend;
540       int count;
541
542       for (idstart = idend = repo->idarraydata + olddeps; *idend++; )   /* find end */
543         ;
544       count = idend - idstart - 1 + num;               /* new size */
545
546       repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, count, sizeof(Id), IDARRAY_BLOCK);
547       /* move old deps to end */
548       olddeps = repo->lastoff = repo->idarraysize;
549       memcpy(repo->idarraydata + olddeps, idstart, count - num);
550       repo->idarraysize = olddeps + count - num;
551
552       return olddeps;
553     }
554
555   if (olddeps)                         /* appending */
556     repo->idarraysize--;
557
558   /* make room*/
559   repo->idarraydata = solv_extend(repo->idarraydata, repo->idarraysize, num, sizeof(Id), IDARRAY_BLOCK);
560
561   /* appending or new */
562   repo->lastoff = olddeps ? olddeps : repo->idarraysize;
563
564   return repo->lastoff;
565 }
566
567
568
569 Offset
570 repo_fix_supplements(Repo *repo, Offset provides, Offset supplements, Offset freshens)
571 {
572   Pool *pool = repo->pool;
573   Id id, idp, idl;
574   char buf[1024], *p, *dep;
575   int i, l;
576
577   if (provides)
578     {
579       for (i = provides; repo->idarraydata[i]; i++)
580         {
581           id = repo->idarraydata[i];
582           if (ISRELDEP(id))
583             continue;
584           dep = (char *)pool_id2str(pool, id);
585           if (!strncmp(dep, "locale(", 7) && strlen(dep) < sizeof(buf) - 2)
586             {
587               idp = 0;
588               strcpy(buf + 2, dep);
589               dep = buf + 2 + 7;
590               if ((p = strchr(dep, ':')) != 0 && p != dep)
591                 {
592                   *p++ = 0;
593                   idp = pool_str2id(pool, dep, 1);
594                   dep = p;
595                 }
596               id = 0;
597               while ((p = strchr(dep, ';')) != 0)
598                 {
599                   if (p == dep)
600                     {
601                       dep = p + 1;
602                       continue;
603                     }
604                   *p++ = 0;
605 #if 0
606                   strncpy(dep - 9, "language:", 9);
607                   idl = pool_str2id(pool, dep - 9, 1);
608 #else
609                   idl = pool_str2id(pool, dep, 1);
610                   idl = pool_rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
611 #endif
612                   if (id)
613                     id = pool_rel2id(pool, id, idl, REL_OR, 1);
614                   else
615                     id = idl;
616                   dep = p;
617                 }
618               if (dep[0] && dep[1])
619                 {
620                   for (p = dep; *p && *p != ')'; p++)
621                     ;
622                   *p = 0;
623 #if 0
624                   strncpy(dep - 9, "language:", 9);
625                   idl = pool_str2id(pool, dep - 9, 1);
626 #else
627                   idl = pool_str2id(pool, dep, 1);
628                   idl = pool_rel2id(pool, NAMESPACE_LANGUAGE, idl, REL_NAMESPACE, 1);
629 #endif
630                   if (id)
631                     id = pool_rel2id(pool, id, idl, REL_OR, 1);
632                   else
633                     id = idl;
634                 }
635               if (idp)
636                 id = pool_rel2id(pool, idp, id, REL_AND, 1);
637               if (id)
638                 supplements = repo_addid_dep(repo, supplements, id, 0);
639             }
640           else if ((p = strchr(dep, ':')) != 0 && p != dep && p[1] == '/' && strlen(dep) < sizeof(buf))
641             {
642               strcpy(buf, dep);
643               p = buf + (p - dep);
644               *p++ = 0;
645               idp = pool_str2id(pool, buf, 1);
646               /* strip trailing slashes */
647               l = strlen(p);
648               while (l > 1 && p[l - 1] == '/')
649                 p[--l] = 0;
650               id = pool_str2id(pool, p, 1);
651               id = pool_rel2id(pool, idp, id, REL_WITH, 1);
652               id = pool_rel2id(pool, NAMESPACE_SPLITPROVIDES, id, REL_NAMESPACE, 1);
653               supplements = repo_addid_dep(repo, supplements, id, 0);
654             }
655         }
656     }
657   if (supplements)
658     {
659       for (i = supplements; repo->idarraydata[i]; i++)
660         {
661           id = repo->idarraydata[i];
662           if (ISRELDEP(id))
663             continue;
664           dep = (char *)pool_id2str(pool, id);
665           if (!strncmp(dep, "system:modalias(", 16))
666             dep += 7;
667           if (!strncmp(dep, "modalias(", 9) && dep[9] && dep[10] && strlen(dep) < sizeof(buf))
668             {
669               strcpy(buf, dep);
670               p = strchr(buf + 9, ':');
671               if (p && p != buf + 9 && strchr(p + 1, ':'))
672                 {
673                   *p++ = 0;
674                   idp = pool_str2id(pool, buf + 9, 1);
675                   p[strlen(p) - 1] = 0;
676                   id = pool_str2id(pool, p, 1);
677                   id = pool_rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
678                   id = pool_rel2id(pool, idp, id, REL_AND, 1);
679                 }
680               else
681                 {
682                   p = buf + 9;
683                   p[strlen(p) - 1] = 0;
684                   id = pool_str2id(pool, p, 1);
685                   id = pool_rel2id(pool, NAMESPACE_MODALIAS, id, REL_NAMESPACE, 1);
686                 }
687               if (id)
688                 repo->idarraydata[i] = id;
689             }
690           else if (!strncmp(dep, "packageand(", 11) && strlen(dep) < sizeof(buf))
691             {
692               strcpy(buf, dep);
693               id = 0;
694               dep = buf + 11;
695               while ((p = strchr(dep, ':')) != 0)
696                 {
697                   if (p == dep)
698                     {
699                       dep = p + 1;
700                       continue;
701                     }
702                   *p++ = 0;
703                   idp = pool_str2id(pool, dep, 1);
704                   if (id)
705                     id = pool_rel2id(pool, id, idp, REL_AND, 1);
706                   else
707                     id = idp;
708                   dep = p;
709                 }
710               if (dep[0] && dep[1])
711                 {
712                   dep[strlen(dep) - 1] = 0;
713                   idp = pool_str2id(pool, dep, 1);
714                   if (id)
715                     id = pool_rel2id(pool, id, idp, REL_AND, 1);
716                   else
717                     id = idp;
718                 }
719               if (id)
720                 repo->idarraydata[i] = id;
721             }
722           else if (!strncmp(dep, "filesystem(", 11) && strlen(dep) < sizeof(buf))
723             {
724               strcpy(buf, dep + 11);
725               if ((p = strrchr(buf, ')')) != 0)
726                 *p = 0;
727               id = pool_str2id(pool, buf, 1);
728               id = pool_rel2id(pool, NAMESPACE_FILESYSTEM, id, REL_NAMESPACE, 1);
729               repo->idarraydata[i] = id;
730             }
731         }
732     }
733   if (freshens && repo->idarraydata[freshens])
734     {
735       Id idsupp = 0, idfresh = 0;
736       if (!supplements)
737         return freshens;
738       for (i = supplements; repo->idarraydata[i]; i++)
739         {
740           if (!idsupp)
741             idsupp = repo->idarraydata[i];
742           else
743             idsupp = pool_rel2id(pool, idsupp, repo->idarraydata[i], REL_OR, 1);
744         }
745       for (i = freshens; repo->idarraydata[i]; i++)
746         {
747           if (!idfresh)
748             idfresh = repo->idarraydata[i];
749           else
750             idfresh = pool_rel2id(pool, idfresh, repo->idarraydata[i], REL_OR, 1);
751         }
752       if (!idsupp)
753         idsupp = idfresh;
754       else
755         idsupp = pool_rel2id(pool, idsupp, idfresh, REL_AND, 1);
756       supplements = repo_addid_dep(repo, 0, idsupp, 0);
757     }
758   return supplements;
759 }
760
761 Offset
762 repo_fix_conflicts(Repo *repo, Offset conflicts)
763 {
764   char buf[1024], *p, *dep;
765   Pool *pool = repo->pool;
766   Id id;
767   int i;
768
769   if (!conflicts)
770     return conflicts;
771   for (i = conflicts; repo->idarraydata[i]; i++)
772     {
773       id = repo->idarraydata[i];
774       if (ISRELDEP(id))
775         continue;
776       dep = (char *)pool_id2str(pool, id);
777       if (!strncmp(dep, "otherproviders(", 15) && strlen(dep) < sizeof(buf) - 2)
778         {
779           strcpy(buf, dep + 15);
780           if ((p = strchr(buf, ')')) != 0)
781             *p = 0;
782           id = pool_str2id(pool, buf, 1);
783           id = pool_rel2id(pool, NAMESPACE_OTHERPROVIDERS, id, REL_NAMESPACE, 1);
784           repo->idarraydata[i] = id;
785         }
786     }
787   return conflicts;
788 }
789
790 struct matchdata
791 {
792   Pool *pool;
793   int flags;
794   Datamatcher matcher;
795   int stop;
796   int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv);
797   void *callback_data;
798 };
799
800 int
801 repo_matchvalue(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
802 {
803   struct matchdata *md = cbdata;
804
805   if (md->matcher.match)
806     {
807       if (!repodata_stringify(md->pool, data, key, kv, md->flags))
808         return 0;
809       if (!datamatcher_match(&md->matcher, kv->str))
810         return 0;
811     }
812   md->stop = md->callback(md->callback_data, s, data, key, kv);
813   return md->stop;
814 }
815
816
817 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
818   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
819   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
820   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
821   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
822   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
823   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
824   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
825   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
826   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
827   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
828   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
829   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
830   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
831 };
832
833 static void
834 domatch_idarray(Solvable *s, Id keyname, struct matchdata *md, Id *ida)
835 {
836   KeyValue kv;
837   kv.entry = 0;
838   kv.parent = 0;
839   for (; *ida && !md->stop; ida++)
840     {
841       kv.id = *ida;
842       kv.eof = ida[1] ? 0 : 1;
843       repo_matchvalue(md, s, 0, solvablekeys + (keyname - SOLVABLE_NAME), &kv);
844       kv.entry++;
845     }
846 }
847
848 static void
849 repo_search_md(Repo *repo, Id p, Id keyname, struct matchdata *md)
850 {
851   KeyValue kv;
852   Pool *pool = repo->pool;
853   Repodata *data;
854   int i, j, flags;
855   Solvable *s;
856
857   kv.parent = 0;
858   md->stop = 0;
859   if (!p)
860     {
861       for (p = repo->start, s = repo->pool->solvables + p; p < repo->end; p++, s++)
862         {
863           if (s->repo == repo)
864             repo_search_md(repo, p, keyname, md);
865           if (md->stop > SEARCH_NEXT_SOLVABLE)
866             break;
867         }
868       return;
869     }
870   else if (p < 0)
871     /* The callback only supports solvables, so we can't iterate over the
872        extra things.  */
873     return;
874   flags = md->flags;
875   if (!(flags & SEARCH_NO_STORAGE_SOLVABLE))
876     {
877       s = pool->solvables + p;
878       switch(keyname)
879         {
880           case 0:
881           case SOLVABLE_NAME:
882             if (s->name)
883               {
884                 kv.id = s->name;
885                 repo_matchvalue(md, s, 0, solvablekeys + 0, &kv);
886               }
887             if (keyname || md->stop > SEARCH_NEXT_KEY)
888               return;
889           case SOLVABLE_ARCH:
890             if (s->arch)
891               {
892                 kv.id = s->arch;
893                 repo_matchvalue(md, s, 0, solvablekeys + 1, &kv);
894               }
895             if (keyname || md->stop > SEARCH_NEXT_KEY)
896               return;
897           case SOLVABLE_EVR:
898             if (s->evr)
899               {
900                 kv.id = s->evr;
901                 repo_matchvalue(md, s, 0, solvablekeys + 2, &kv);
902               }
903             if (keyname || md->stop > SEARCH_NEXT_KEY)
904               return;
905           case SOLVABLE_VENDOR:
906             if (s->vendor)
907               {
908                 kv.id = s->vendor;
909                 repo_matchvalue(md, s, 0, solvablekeys + 3, &kv);
910               }
911             if (keyname || md->stop > SEARCH_NEXT_KEY)
912               return;
913           case SOLVABLE_PROVIDES:
914             if (s->provides)
915               domatch_idarray(s, SOLVABLE_PROVIDES, md, repo->idarraydata + s->provides);
916             if (keyname || md->stop > SEARCH_NEXT_KEY)
917               return;
918           case SOLVABLE_OBSOLETES:
919             if (s->obsoletes)
920               domatch_idarray(s, SOLVABLE_OBSOLETES, md, repo->idarraydata + s->obsoletes);
921             if (keyname || md->stop > SEARCH_NEXT_KEY)
922               return;
923           case SOLVABLE_CONFLICTS:
924             if (s->conflicts)
925               domatch_idarray(s, SOLVABLE_CONFLICTS, md, repo->idarraydata + s->conflicts);
926             if (keyname || md->stop > SEARCH_NEXT_KEY)
927               return;
928           case SOLVABLE_REQUIRES:
929             if (s->requires)
930               domatch_idarray(s, SOLVABLE_REQUIRES, md, repo->idarraydata + s->requires);
931             if (keyname || md->stop > SEARCH_NEXT_KEY)
932               return;
933           case SOLVABLE_RECOMMENDS:
934             if (s->recommends)
935               domatch_idarray(s, SOLVABLE_RECOMMENDS, md, repo->idarraydata + s->recommends);
936             if (keyname || md->stop > SEARCH_NEXT_KEY)
937               return;
938           case SOLVABLE_SUPPLEMENTS:
939             if (s->supplements)
940               domatch_idarray(s, SOLVABLE_SUPPLEMENTS, md, repo->idarraydata + s->supplements);
941             if (keyname || md->stop > SEARCH_NEXT_KEY)
942               return;
943           case SOLVABLE_SUGGESTS:
944             if (s->suggests)
945               domatch_idarray(s, SOLVABLE_SUGGESTS, md, repo->idarraydata + s->suggests);
946             if (keyname || md->stop > SEARCH_NEXT_KEY)
947               return;
948           case SOLVABLE_ENHANCES:
949             if (s->enhances)
950               domatch_idarray(s, SOLVABLE_ENHANCES, md, repo->idarraydata + s->enhances);
951             if (keyname || md->stop > SEARCH_NEXT_KEY)
952               return;
953           case RPM_RPMDBID:
954             if (repo->rpmdbid)
955               {
956                 kv.num = repo->rpmdbid[p - repo->start];
957                 repo_matchvalue(md, s, 0, solvablekeys + (RPM_RPMDBID - SOLVABLE_NAME), &kv);
958               }
959             if (keyname || md->stop > SEARCH_NEXT_KEY)
960               return;
961             break;
962           default:
963             break;
964         }
965     }
966
967   FOR_REPODATAS(repo, i, data)
968     {
969       if (p < data->start || p >= data->end)
970         continue;
971       if (keyname && !repodata_precheck_keyname(data, keyname))
972         continue;
973       if (keyname == SOLVABLE_FILELIST && !(md->flags & SEARCH_COMPLETE_FILELIST))
974         {
975           /* do not search filelist extensions */
976           if (data->state != REPODATA_AVAILABLE)
977             continue;
978           for (j = 1; j < data->nkeys; j++)
979             if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
980               break;
981           if (j == data->nkeys)
982             continue;
983         }
984       if (data->state == REPODATA_STUB)
985         {
986           if (keyname)
987             {
988               for (j = 1; j < data->nkeys; j++)
989                 if (keyname == data->keys[j].name)
990                   break;
991               if (j == data->nkeys)
992                 continue;
993             }
994           /* load it */
995           if (data->loadcallback)
996             data->loadcallback(data);
997           else
998             data->state = REPODATA_ERROR;
999         }
1000       if (data->state == REPODATA_ERROR)
1001         continue;
1002       repodata_search(data, p, keyname, md->flags, repo_matchvalue, md);
1003       if (md->stop > SEARCH_NEXT_KEY)
1004         break;
1005     }
1006 }
1007
1008 void
1009 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)
1010 {
1011   struct matchdata md;
1012
1013   if (repo->disabled && !(flags & SEARCH_DISABLED_REPOS))
1014     return;
1015   memset(&md, 0, sizeof(md));
1016   md.pool = repo->pool;
1017   md.flags = flags;
1018   md.callback = callback;
1019   md.callback_data = cbdata;
1020   if (match)
1021     datamatcher_init(&md.matcher, match, flags);
1022   repo_search_md(repo, p, keyname, &md);
1023   if (match)
1024     datamatcher_free(&md.matcher);
1025 }
1026
1027 const char *
1028 repo_lookup_str(Repo *repo, Id entry, Id keyname)
1029 {
1030   Pool *pool = repo->pool;
1031   Repodata *data;
1032   int i;
1033   const char *str;
1034
1035   if (entry >= 0)
1036     {
1037       switch (keyname)
1038         {
1039         case SOLVABLE_NAME:
1040           return pool_id2str(pool, pool->solvables[entry].name);
1041         case SOLVABLE_ARCH:
1042           return pool_id2str(pool, pool->solvables[entry].arch);
1043         case SOLVABLE_EVR:
1044           return pool_id2str(pool, pool->solvables[entry].evr);
1045         case SOLVABLE_VENDOR:
1046           return pool_id2str(pool, pool->solvables[entry].vendor);
1047         }
1048     }
1049   FOR_REPODATAS(repo, i, data)
1050     {
1051       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1052         continue;
1053       if (!repodata_precheck_keyname(data, keyname))
1054         continue;
1055       str = repodata_lookup_str(data, entry, keyname);
1056       if (str)
1057         return str;
1058       if (repodata_lookup_type(data, entry, keyname))
1059         return 0;
1060     }
1061   return 0;
1062 }
1063
1064
1065 unsigned int
1066 repo_lookup_num(Repo *repo, Id entry, Id keyname, unsigned int notfound)
1067 {
1068   Repodata *data;
1069   int i;
1070   unsigned int value;
1071
1072   if (entry >= 0)
1073     {
1074       if (keyname == RPM_RPMDBID)
1075         {
1076           if (repo->rpmdbid && entry >= repo->start && entry < repo->end)
1077             return repo->rpmdbid[entry - repo->start];
1078           return notfound;
1079         }
1080     }
1081   FOR_REPODATAS(repo, i, data)
1082     {
1083       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1084         continue;
1085       if (!repodata_precheck_keyname(data, keyname))
1086         continue;
1087       if (repodata_lookup_num(data, entry, keyname, &value))
1088         return value;
1089       if (repodata_lookup_type(data, entry, keyname))
1090         return notfound;
1091     }
1092   return notfound;
1093 }
1094
1095 Id
1096 repo_lookup_id(Repo *repo, Id entry, Id keyname)
1097 {
1098   Repodata *data;
1099   int i;
1100   Id id;
1101
1102   if (entry >= 0)
1103     {
1104       switch (keyname)
1105         {
1106         case SOLVABLE_NAME:
1107           return repo->pool->solvables[entry].name;
1108         case SOLVABLE_ARCH:
1109           return repo->pool->solvables[entry].arch;
1110         case SOLVABLE_EVR:
1111           return repo->pool->solvables[entry].evr;
1112         case SOLVABLE_VENDOR:
1113           return repo->pool->solvables[entry].vendor;
1114         }
1115     }
1116   FOR_REPODATAS(repo, i, data)
1117     {
1118       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1119         continue;
1120       if (!repodata_precheck_keyname(data, keyname))
1121         continue;
1122       id = repodata_lookup_id(data, entry, keyname);
1123       if (id)
1124         return data->localpool ? repodata_globalize_id(data, id, 1) : id;
1125       if (repodata_lookup_type(data, entry, keyname))
1126         return 0;
1127     }
1128   return 0;
1129 }
1130
1131 static int
1132 lookup_idarray_solvable(Repo *repo, Offset off, Queue *q)
1133 {
1134   Id *p;
1135
1136   queue_empty(q);
1137   if (off)
1138     for (p = repo->idarraydata + off; *p; p++)
1139       queue_push(q, *p);
1140   return 1;
1141 }
1142
1143 int
1144 repo_lookup_idarray(Repo *repo, Id entry, Id keyname, Queue *q)
1145 {
1146   Repodata *data;
1147   int i;
1148   if (entry >= 0)
1149     {
1150       switch (keyname)
1151         {
1152         case SOLVABLE_PROVIDES:
1153           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].provides, q);
1154         case SOLVABLE_OBSOLETES:
1155           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].obsoletes, q);
1156         case SOLVABLE_CONFLICTS:
1157           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].conflicts, q);
1158         case SOLVABLE_REQUIRES:
1159           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].requires, q);
1160         case SOLVABLE_RECOMMENDS:
1161           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].recommends, q);
1162         case SOLVABLE_SUGGESTS:
1163           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].suggests, q);
1164         case SOLVABLE_SUPPLEMENTS:
1165           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].supplements, q);
1166         case SOLVABLE_ENHANCES:
1167           return lookup_idarray_solvable(repo, repo->pool->solvables[entry].enhances, q);
1168         }
1169     }
1170   FOR_REPODATAS(repo, i, data)
1171     {
1172       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1173         continue;
1174       if (!repodata_precheck_keyname(data, keyname))
1175         continue;
1176       if (repodata_lookup_idarray(data, entry, keyname, q))
1177         {
1178           if (data->localpool)
1179             {
1180               for (i = 0; i < q->count; i++)
1181                 q->elements[i] = repodata_globalize_id(data, q->elements[i], 1);
1182             }
1183           return 1;
1184         }
1185       if (repodata_lookup_type(data, entry, keyname))
1186         break;
1187     }
1188   queue_empty(q);
1189   return 0;
1190 }
1191
1192 const unsigned char *
1193 repo_lookup_bin_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
1194 {
1195   Repodata *data;
1196   int i;
1197   const unsigned char *chk;
1198
1199   FOR_REPODATAS(repo, i, data)
1200     {
1201       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1202         continue;
1203       if (!repodata_precheck_keyname(data, keyname))
1204         continue;
1205       chk = repodata_lookup_bin_checksum(data, entry, keyname, typep);
1206       if (chk)
1207         return chk;
1208       if (repodata_lookup_type(data, entry, keyname))
1209         return 0;
1210     }
1211   *typep = 0;
1212   return 0;
1213 }
1214
1215 const char *
1216 repo_lookup_checksum(Repo *repo, Id entry, Id keyname, Id *typep)
1217 {
1218   const unsigned char *chk = repo_lookup_bin_checksum(repo, entry, keyname, typep);
1219   return chk ? pool_bin2hex(repo->pool, chk, solv_chksum_len(*typep)) : 0;
1220 }
1221
1222 int
1223 repo_lookup_void(Repo *repo, Id entry, Id keyname)
1224 {
1225   Repodata *data;
1226   int i;
1227   Id type;
1228
1229   FOR_REPODATAS(repo, i, data)
1230     {
1231       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1232         continue;
1233       if (!repodata_precheck_keyname(data, keyname))
1234         continue;
1235       type = repodata_lookup_type(data, entry, keyname);
1236       if (type)
1237         return type == REPOKEY_TYPE_VOID;
1238     }
1239   return 0;
1240 }
1241
1242 Id
1243 repo_lookup_type(Repo *repo, Id entry, Id keyname)
1244 {
1245   Repodata *data;
1246   int i;
1247   Id type;
1248
1249   FOR_REPODATAS(repo, i, data)
1250     {
1251       if (entry != SOLVID_META && (entry < data->start || entry >= data->end))
1252         continue;
1253       if (!repodata_precheck_keyname(data, keyname))
1254         continue;
1255       type = repodata_lookup_type(data, entry, keyname);
1256       if (type)
1257         return type == REPOKEY_TYPE_DELETED ? 0 : type;
1258     }
1259   return 0;
1260 }
1261
1262 /***********************************************************************/
1263
1264 Repodata *
1265 repo_add_repodata(Repo *repo, int flags)
1266 {
1267   int i;
1268   if ((flags & REPO_USE_LOADING) != 0)
1269     {
1270       for (i = repo->nrepodata - 1; i >= 0; i--)
1271         if (repo->repodata[i].state == REPODATA_LOADING)
1272           {
1273             Repodata *data = repo->repodata + i;
1274             /* re-init */
1275             /* hack: we mis-use REPO_REUSE_REPODATA here */
1276             if (!(flags & REPO_REUSE_REPODATA))
1277               repodata_empty(data, (flags & REPO_LOCALPOOL) ? 1 : 0);
1278             return data;
1279           }
1280       return 0; /* must not create a new repodata! */
1281     }
1282   if ((flags & REPO_REUSE_REPODATA) != 0)
1283     {
1284       for (i = repo->nrepodata - 1; i >= 0; i--)
1285         if (repo->repodata[i].state != REPODATA_STUB)
1286           return repo->repodata + i;
1287     }
1288   return repodata_create(repo, (flags & REPO_LOCALPOOL) ? 1 : 0);
1289 }
1290
1291 Repodata *
1292 repo_last_repodata(Repo *repo)
1293 {
1294   int i;
1295   for (i = repo->nrepodata - 1; i >= 0; i--)
1296     if (repo->repodata[i].state != REPODATA_STUB)
1297       return repo->repodata + i;
1298   return repo_add_repodata(repo, 0);
1299 }
1300
1301 void
1302 repo_set_id(Repo *repo, Id p, Id keyname, Id id)
1303 {
1304   Repodata *data;
1305   if (p >= 0)
1306     {
1307       switch (keyname)
1308         {
1309         case SOLVABLE_NAME:
1310           repo->pool->solvables[p].name = id;
1311           return;
1312         case SOLVABLE_ARCH:
1313           repo->pool->solvables[p].arch = id;
1314           return;
1315         case SOLVABLE_EVR:
1316           repo->pool->solvables[p].evr = id;
1317           return;
1318         case SOLVABLE_VENDOR:
1319           repo->pool->solvables[p].vendor = id;
1320           return;
1321         }
1322     }
1323   data = repo_last_repodata(repo);
1324   if (data->localpool)
1325     id = repodata_localize_id(data, id, 1);
1326   repodata_set_id(data, p, keyname, id);
1327 }
1328
1329 void
1330 repo_set_num(Repo *repo, Id p, Id keyname, unsigned int num)
1331 {
1332   Repodata *data;
1333   if (p >= 0)
1334     {
1335       if (keyname == RPM_RPMDBID)
1336         {
1337           if (!repo->rpmdbid)
1338             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1339           repo->rpmdbid[p] = num;
1340           return;
1341         }
1342     }
1343   data = repo_last_repodata(repo);
1344   repodata_set_num(data, p, keyname, num);
1345 }
1346
1347 void
1348 repo_set_str(Repo *repo, Id p, Id keyname, const char *str)
1349 {
1350   Repodata *data;
1351   if (p >= 0)
1352     {
1353       switch (keyname)
1354         {
1355         case SOLVABLE_NAME:
1356         case SOLVABLE_ARCH:
1357         case SOLVABLE_EVR:
1358         case SOLVABLE_VENDOR:
1359           repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
1360           return;
1361         }
1362     }
1363   data = repo_last_repodata(repo);
1364   repodata_set_str(data, p, keyname, str);
1365 }
1366
1367 void
1368 repo_set_poolstr(Repo *repo, Id p, Id keyname, const char *str)
1369 {
1370   Repodata *data;
1371   if (p >= 0)
1372     {
1373       switch (keyname)
1374         {
1375         case SOLVABLE_NAME:
1376         case SOLVABLE_ARCH:
1377         case SOLVABLE_EVR:
1378         case SOLVABLE_VENDOR:
1379           repo_set_id(repo, p, keyname, pool_str2id(repo->pool, str, 1));
1380           return;
1381         }
1382     }
1383   data = repo_last_repodata(repo);
1384   repodata_set_poolstr(data, p, keyname, str);
1385 }
1386
1387 void
1388 repo_add_poolstr_array(Repo *repo, Id p, Id keyname, const char *str)
1389 {
1390   Repodata *data = repo_last_repodata(repo);
1391   repodata_add_poolstr_array(data, p, keyname, str);
1392 }
1393
1394 void
1395 repo_internalize(Repo *repo)
1396 {
1397   int i;
1398   Repodata *data;
1399
1400   FOR_REPODATAS(repo, i, data)
1401     if (data->attrs || data->xattrs)
1402       repodata_internalize(data);
1403 }
1404
1405 void
1406 repo_disable_paging(Repo *repo)
1407 {
1408   int i;
1409   Repodata *data;
1410
1411   FOR_REPODATAS(repo, i, data)
1412     repodata_disable_paging(data);
1413 }
1414 // EOF
1415 /*
1416 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1417 */