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