06e97f3c5804aabb9e41446639d0ed10619cd9a4
[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 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "repo.h"
20 #include "pool.h"
21 #include "poolid_private.h"
22 #include "util.h"
23 #include "attr_store_p.h"
24
25 #define IDARRAY_BLOCK     4095
26
27
28 /*
29  * create empty repo
30  * and add to pool
31  */
32
33 Repo *
34 repo_create(Pool *pool, const char *name)
35 {
36   Repo *repo;
37
38   pool_freewhatprovides(pool);
39   repo = (Repo *)xcalloc(1, sizeof(*repo));
40   pool->repos = (Repo **)xrealloc(pool->repos, (pool->nrepos + 1) * sizeof(Repo *));
41   pool->repos[pool->nrepos++] = repo;
42   repo->name = name ? strdup(name) : 0;
43   repo->pool = pool;
44   repo->start = pool->nsolvables;
45   repo->end = pool->nsolvables;
46   repo->nsolvables = 0;
47   return repo;
48 }
49
50 static void
51 repo_freedata(Repo *repo)
52 {
53   xfree(repo->idarraydata);
54   xfree(repo->rpmdbid);
55   xfree((char *)repo->name);
56   xfree(repo);
57 }
58
59 /*
60  * add Id to repo
61  * olddeps = old array to extend
62  * 
63  */
64
65 Offset
66 repo_addid(Repo *repo, Offset olddeps, Id id)
67 {
68   Id *idarray;
69   int idarraysize;
70   int i;
71   
72   idarray = repo->idarraydata;
73   idarraysize = repo->idarraysize;
74
75   if (!idarray)                        /* alloc idarray if not done yet */
76     {
77       idarray = (Id *)xmalloc((1 + IDARRAY_BLOCK) * sizeof(Id));
78       idarray[0] = 0;
79       idarraysize = 1;
80       repo->lastoff = 0;
81     }
82
83   if (!olddeps)                         /* no deps yet */
84     {   
85       olddeps = idarraysize;
86       if ((idarraysize & IDARRAY_BLOCK) == 0)
87         idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
88     }   
89   else if (olddeps == repo->lastoff)    /* extend at end */
90     idarraysize--;
91   else                                  /* can't extend, copy old */
92     {
93       i = olddeps;
94       olddeps = idarraysize;
95       for (; idarray[i]; i++)
96         {
97           if ((idarraysize & IDARRAY_BLOCK) == 0)
98             idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
99           idarray[idarraysize++] = idarray[i];
100         }
101       if ((idarraysize & IDARRAY_BLOCK) == 0)
102         idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
103     }
104   
105   idarray[idarraysize++] = id;          /* insert Id into array */
106
107   if ((idarraysize & IDARRAY_BLOCK) == 0)   /* realloc if at block boundary */
108     idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
109
110   idarray[idarraysize++] = 0;           /* ensure NULL termination */
111
112   repo->idarraydata = idarray;
113   repo->idarraysize = idarraysize;
114   repo->lastoff = olddeps;
115
116   return olddeps;
117 }
118
119
120 /*
121  * add dependency (as Id) to repo
122  * olddeps = offset into idarraydata
123  * isreq = 0 for normal dep
124  * isreq = 1 for requires
125  * isreq = 2 for pre-requires
126  * 
127  */
128
129 Offset
130 repo_addid_dep(Repo *repo, Offset olddeps, Id id, int isreq)
131 {
132   Id oid, *oidp, *marker = 0;
133
134   if (!olddeps)
135     return repo_addid(repo, olddeps, id);
136
137   if (!isreq)
138     {
139       for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != ID_NULL; oidp++)
140         {
141           if (oid == id)
142             return olddeps;
143         }
144       return repo_addid(repo, olddeps, id);
145     }
146
147   for (oidp = repo->idarraydata + olddeps; (oid = *oidp) != ID_NULL; oidp++)
148     {
149       if (oid == SOLVABLE_PREREQMARKER)
150         marker = oidp;
151       else if (oid == id)
152         break;
153     }
154
155   if (oid)
156     {
157       if (marker || isreq == 1)
158         return olddeps;
159       marker = oidp++;
160       for (; (oid = *oidp) != ID_NULL; oidp++)
161         if (oid == SOLVABLE_PREREQMARKER)
162           break;
163       if (!oid)
164         {
165           oidp--;
166           if (marker < oidp)
167             memmove(marker, marker + 1, (oidp - marker) * sizeof(Id));
168           *oidp = SOLVABLE_PREREQMARKER;
169           return repo_addid(repo, olddeps, id);
170         }
171       while (oidp[1])
172         oidp++;
173       memmove(marker, marker + 1, (oidp - marker) * sizeof(Id));
174       *oidp = id;
175       return olddeps;
176     }
177   if (isreq == 2 && !marker)
178     olddeps = repo_addid(repo, olddeps, SOLVABLE_PREREQMARKER);
179   else if (isreq == 1 && marker)
180     {
181       *marker++ = id;
182       id = *--oidp;
183       if (marker < oidp)
184         memmove(marker + 1, marker, (oidp - marker) * sizeof(Id));
185       *marker = SOLVABLE_PREREQMARKER;
186     }
187   return repo_addid(repo, olddeps, id);
188 }
189
190
191 /*
192  * reserve Ids
193  * make space for 'num' more dependencies
194  */
195
196 Offset
197 repo_reserve_ids(Repo *repo, Offset olddeps, int num)
198 {
199   num++;        /* room for trailing ID_NULL */
200
201   if (!repo->idarraysize)              /* ensure buffer space */
202     {
203       repo->idarraysize = 1;
204       repo->idarraydata = (Id *)xmalloc(((1 + num + IDARRAY_BLOCK) & ~IDARRAY_BLOCK) * sizeof(Id));
205       repo->idarraydata[0] = 0;
206       repo->lastoff = 1;
207       return 1;
208     }
209
210   if (olddeps && olddeps != repo->lastoff)   /* if not appending */
211     {
212       /* can't insert into idarray, this would invalidate all 'larger' offsets
213        * so create new space at end and move existing deps there.
214        * Leaving 'hole' at old position.
215        */
216       
217       Id *idstart, *idend;
218       int count;
219
220       for (idstart = idend = repo->idarraydata + olddeps; *idend++; )   /* find end */
221         ;
222       count = idend - idstart - 1 + num;               /* new size */
223
224       /* realloc if crossing block boundary */
225       if (((repo->idarraysize - 1) | IDARRAY_BLOCK) != ((repo->idarraysize + count - 1) | IDARRAY_BLOCK))
226         repo->idarraydata = (Id *)xrealloc(repo->idarraydata, ((repo->idarraysize + count + IDARRAY_BLOCK) & ~IDARRAY_BLOCK) * sizeof(Id));
227
228       /* move old deps to end */
229       olddeps = repo->lastoff = repo->idarraysize;
230       memcpy(repo->idarraydata + olddeps, idstart, count - num);
231       repo->idarraysize = olddeps + count - num;
232
233       return olddeps;
234     }
235
236   if (olddeps)                         /* appending */
237     repo->idarraysize--;
238
239   /* realloc if crossing block boundary */
240   if (((repo->idarraysize - 1) | IDARRAY_BLOCK) != ((repo->idarraysize + num - 1) | IDARRAY_BLOCK))
241     repo->idarraydata = (Id *)xrealloc(repo->idarraydata, ((repo->idarraysize + num + IDARRAY_BLOCK) & ~IDARRAY_BLOCK) * sizeof(Id));
242
243   /* appending or new */
244   repo->lastoff = olddeps ? olddeps : repo->idarraysize;
245
246   return repo->lastoff;
247 }
248
249
250 /*
251  * remove repo from pool, zero out solvables 
252  * 
253  */
254
255 void
256 repo_free(Repo *repo, int reuseids)
257 {
258   Pool *pool = repo->pool;
259   Solvable *s;
260   int i;
261
262   pool_freewhatprovides(pool);
263
264   if (reuseids && repo->end == pool->nsolvables)
265     {
266       /* it's ok to reuse the ids. As this is the last repo, we can
267          just shrink the solvable array */
268       for (i = repo->end - 1, s = pool->solvables + i; i >= repo->start; i--, s--)
269         if (s->repo != repo)
270           break;
271       repo->end = i + 1;
272       pool->nsolvables = i + 1;
273     }
274   /* zero out solvables belonging to this repo */
275   for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
276     if (s->repo == repo)
277       memset(s, 0, sizeof(*s));
278   for (i = 0; i < pool->nrepos; i++)    /* find repo in pool */
279     if (pool->repos[i] == repo)
280       break;
281   if (i == pool->nrepos)               /* repo not in pool, return */
282     return;
283   if (i < pool->nrepos - 1)
284     memmove(pool->repos + i, pool->repos + i + 1, (pool->nrepos - 1 - i) * sizeof(Repo *));
285   pool->nrepos--;
286   repo_freedata(repo);
287 }
288
289 void
290 repo_freeallrepos(Pool *pool, int reuseids)
291 {
292   int i;
293
294   pool_freewhatprovides(pool);
295   for (i = 0; i < pool->nrepos; i++)
296     repo_freedata(pool->repos[i]);
297   pool->repos = xfree(pool->repos);
298   pool->nrepos = 0;
299   /* the first two solvables don't belong to a repo */
300   pool_free_solvable_block(pool, 2, pool->nsolvables - 2, reuseids);
301 }
302
303 Offset
304 repo_fix_legacy(Repo *repo, Offset provides, Offset supplements)
305 {
306   Pool *pool = repo->pool;
307   Id id, idp, idl, idns;
308   char buf[1024], *p, *dep;
309   int i;
310
311   if (provides)
312     {
313       for (i = provides; repo->idarraydata[i]; i++)
314         {
315           id = repo->idarraydata[i];
316           if (ISRELDEP(id))
317             continue;
318           dep = (char *)id2str(pool, id);
319           if (!strncmp(dep, "locale(", 7) && strlen(dep) < sizeof(buf) - 2)
320             {
321               idp = 0;
322               strcpy(buf + 2, dep);
323               dep = buf + 2 + 7;
324               if ((p = strchr(dep, ':')) != 0 && p != dep)
325                 {
326                   *p++ = 0;
327                   idp = str2id(pool, dep, 1);
328                   dep = p;
329                 }
330               id = 0;
331               while ((p = strchr(dep, ';')) != 0)
332                 {
333                   if (p == dep)
334                     {
335                       dep = p + 1;
336                       continue;
337                     }
338                   strncpy(dep - 9, "language:", 9);
339                   *p++ = 0;
340                   idl = str2id(pool, dep - 9, 1);
341                   if (id)
342                     id = rel2id(pool, id, idl, REL_OR, 1);
343                   else
344                     id = idl;
345                   dep = p;
346                 }
347               if (dep[0] && dep[1])
348                 {
349                   for (p = dep; *p && *p != ')'; p++)
350                     ;
351                   *p = 0;
352                   strncpy(dep - 9, "language:", 9);
353                   idl = str2id(pool, dep - 9, 1);
354                   if (id)
355                     id = rel2id(pool, id, idl, REL_OR, 1);
356                   else
357                     id = idl;
358                 }
359               if (idp)
360                 id = rel2id(pool, idp, id, REL_AND, 1);
361               if (id)
362                 supplements = repo_addid_dep(repo, supplements, id, 0);
363             }
364           else if ((p = strchr(dep, ':')) != 0 && p != dep && p[1] == '/' && strlen(dep) < sizeof(buf))
365             {
366               strcpy(buf, dep);
367               p = buf + (p - dep);
368               *p++ = 0;
369               idp = str2id(pool, buf, 1);
370               idns = str2id(pool, "namespace:installed", 1);
371               id = str2id(pool, p, 1);
372               id = rel2id(pool, idns, id, REL_NAMESPACE, 1);
373               id = rel2id(pool, idp, id, REL_AND, 1);
374               supplements = repo_addid_dep(repo, supplements, id, 0);
375             }
376         }
377     }
378   if (!supplements)
379     return 0;
380   for (i = supplements; repo->idarraydata[i]; i++)
381     {
382       id = repo->idarraydata[i];
383       if (ISRELDEP(id))
384         continue;
385       dep = (char *)id2str(pool, id);
386       if (!strncmp(dep, "system:modalias(", 16))
387         dep += 7;
388       if (!strncmp(dep, "modalias(", 9) && dep[9] && dep[10] && strlen(dep) < sizeof(buf))
389         {
390           strcpy(buf, dep);
391           p = strchr(buf + 9, ':');
392           idns = str2id(pool, "namespace:modalias", 1);
393           if (p && p != buf + 9 && strchr(p + 1, ':'))
394             {
395               *p++ = 0;
396               idp = str2id(pool, buf + 9, 1);
397               p[strlen(p) - 1] = 0;
398               id = str2id(pool, p, 1);
399               id = rel2id(pool, idns, id, REL_NAMESPACE, 1);
400               id = rel2id(pool, idp, id, REL_AND, 1);
401             }
402           else
403             {
404               p = buf + 9;
405               p[strlen(p) - 1] = 0;
406               id = str2id(pool, p, 1);
407               id = rel2id(pool, idns, id, REL_NAMESPACE, 1);
408             }
409           if (id)
410             repo->idarraydata[i] = id;
411         }
412       else if (!strncmp(dep, "packageand(", 11) && strlen(dep) < sizeof(buf))
413         {
414           strcpy(buf, dep);
415           id = 0;
416           dep = buf + 11;
417           while ((p = strchr(dep, ':')) != 0)
418             {
419               if (p == dep)
420                 {
421                   dep = p + 1;
422                   continue;
423                 }
424               *p++ = 0;
425               idp = str2id(pool, dep, 1);
426               if (id)
427                 id = rel2id(pool, id, idp, REL_AND, 1);
428               else
429                 id = idp;
430               dep = p;
431             }
432           if (dep[0] && dep[1])
433             {
434               dep[strlen(dep) - 1] = 0;
435               idp = str2id(pool, dep, 1);
436               if (id)
437                 id = rel2id(pool, id, idp, REL_AND, 1);
438               else
439                 id = idp;
440             }
441           if (id)
442             repo->idarraydata[i] = id;
443         }
444     }
445   return supplements;
446 }
447
448 static unsigned char *
449 data_read_id(unsigned char *dp, Id *idp)
450 {
451   Id x = 0;
452   unsigned char c;
453   for (;;)
454     {
455       c = *dp++;
456       if (!(c & 0x80))
457         {
458           *idp = (x << 7) ^ c;
459           return dp;
460         }
461       x = (x << 7) ^ c ^ 128;
462     }
463 }
464
465 static unsigned char *
466 data_skip(unsigned char *dp, int type)
467 {
468   switch (type)
469     {
470     case TYPE_VOID:
471       return dp;
472     case TYPE_ID:
473       while ((*dp & 0x80) != 0)
474         dp++;
475       return dp;
476     case TYPE_IDARRAY:
477     case TYPE_REL_IDARRAY:
478     case TYPE_IDVALUEARRAY:
479     case TYPE_IDVALUEVALUEARRAY:
480       while ((*dp & 0xc0) != 0)
481         dp++;
482       return dp;
483     default:
484       fprintf(stderr, "unknown type in data_skip\n");
485       exit(1);
486     }
487 }
488
489 static unsigned char *
490 forward_to_key(Repodata *data, Id key, Id schema, unsigned char *dp)
491 {
492   Id k, *keyp;
493
494   keyp = data->schemadata + schema;
495   while ((k = *keyp++) != 0)
496     {
497       if (k == key)
498         return dp;
499       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
500         {
501           /* skip that offset */
502           dp = data_skip(dp, TYPE_ID);
503           continue;
504         }
505       if (data->keys[k].storage != KEY_STORAGE_INCORE)
506         continue;
507       dp = data_skip(dp, data->keys[k].type);
508     }
509   return 0;
510 }
511
512 static unsigned char *
513 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
514 {
515   /* add smart paging here */
516   return 0;
517 }
518
519 static unsigned char *
520 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
521 {
522   Id off;
523   unsigned char *dp = *dpp;
524   int i, max;
525   unsigned int pstart, pend, poff, plen;
526
527   if (!dp)
528     return 0;
529   if (key->storage == KEY_STORAGE_INCORE)
530     {
531       *dpp = data_skip(dp, key->type);
532       return dp;
533     }
534   if (key->storage != KEY_STORAGE_VERTICAL_OFFSET)
535     return 0;
536   if (!data->fp)
537     return 0;
538   dp = data_read_id(dp, &off);
539   *dpp = dp;
540   if (key->type == TYPE_VOID)
541     return 0;
542   max = key->size - off;
543   if (max <= 0)
544     return 0;
545   /* we now have the offset, go into vertical */
546   for (i = key - data->keys - 1; i > 0; i--)
547     if (data->keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
548       off += data->keys[i].size;
549   pstart = off / BLOB_PAGESIZE;
550   pend = pstart;
551   poff = off % BLOB_PAGESIZE;
552   plen = BLOB_PAGESIZE - poff;
553   for (;;)
554     {
555       if (plen > max)
556         plen = max;
557       dp = load_page_range(data, pstart, pend) + poff;
558       if (!dp)
559         return 0;
560       switch (key->type)
561         {
562         case TYPE_STR:
563           if (memchr(dp, 0, plen))
564             return dp;
565           break;
566         case TYPE_ID:
567           for (i = 0; i < plen; i++)
568             if ((dp[i] & 0x80) == 0)
569               return dp;
570           break;
571         case TYPE_IDARRAY:
572         case TYPE_REL_IDARRAY:
573         case TYPE_IDVALUEARRAY:
574         case TYPE_IDVALUEVALUEARRAY:
575           for (i = 0; i < plen; i++)
576             if ((dp[i] & 0xc0) == 0)
577               return dp;
578           break;
579         }
580       if (plen == max)
581         return 0;
582       pend++;
583       plen += BLOB_PAGESIZE;
584     }
585 }
586
587
588 const char *
589 repodata_lookup_str(Repodata *data, Id entry, Id key)
590 {
591   Id schema;
592   Id id, k, *kp, *keyp;
593   unsigned char *dp;
594
595   if (data->entryschemau8)
596     schema = data->entryschemau8[entry];
597   else
598     schema = data->entryschema[entry];
599   keyp = data->schemadata + schema;
600   /* make sure the schema of this solvable contains the key */
601   for (kp = keyp; (k = *kp++) != 0; )
602     if (k == key)
603       break;
604   if (k == 0)
605     return 0;
606   dp = forward_to_key(data, key, schema, data->incoredata + data->incoreoffset[entry]);
607   dp = get_data(data, data->keys + key, &dp);
608   if (!dp)
609     return 0;
610   if (data->keys[key].type == TYPE_STR)
611     return (const char *)dp;
612   /* id type, must either use global or local string strore*/
613   dp = data_read_id(dp, &id);
614 #if 0
615   /* not yet working */
616   return data->ss.stringspace + data->ss.strings[id];
617 #else
618   return id2str(data->repo->pool, id);
619 #endif
620 }
621
622 #define SEARCH_NEXT_KEY         1
623 #define SEARCH_NEXT_SOLVABLE    2
624 #define SEACH_STOP              3
625
626 struct matchdata
627 {
628   Pool *pool;
629   const char *matchstr;
630   int flags;
631 #if 0
632   regex_t regex;
633 #endif
634   int stop;
635   int (*callback)(void *data, Solvable *s, Id key, const char *str);
636   void *callback_data;
637 };
638
639 static void
640 domatch(Id p, Id key, struct matchdata *md, const char *str)
641 {
642   /* fill match code here */
643   md->stop = md->callback(md->callback_data, md->pool->solvables + p, key, str);
644 }
645
646 static void
647 repodata_search(Repodata *data, Id entry, Id key, struct matchdata *md)
648 {
649   Id schema;
650   Id id, k, *kp, *keyp;
651   unsigned char *dp, *ddp;
652   int onekey = 0;
653
654   if (data->entryschemau8)
655     schema = data->entryschemau8[entry];
656   else
657     schema = data->entryschema[entry];
658   keyp = data->schemadata + schema;
659   dp = data->incoredata + data->incoreoffset[entry];
660   if (key)
661     {
662       /* search in a specific key */
663       for (kp = keyp; (k = *kp++) != 0; )
664         if (k == key)
665           break;
666       if (k == 0)
667         return;
668       dp = forward_to_key(data, key, schema, dp);
669       if (!dp)
670         return;
671       keyp = kp - 1;
672       onekey = 1;
673     }
674   md->stop = 0;
675   while ((key = *keyp++) != 0)
676     {
677       ddp = get_data(data, data->keys + key, &dp);
678       while (ddp)
679         {
680           switch (data->keys[key].type)
681             {
682             case TYPE_STR:
683               domatch(data->start + entry, data->keys[key].name, md, (const char *)ddp);
684               ddp = 0;
685               break;
686             case TYPE_ID:
687               data_read_id(ddp, &id);
688               /* domatch */
689               ddp = 0;
690               break;
691             case TYPE_IDARRAY:
692               ddp = data_read_id(ddp, &id);
693               if ((id & 0x40) == 0)
694                 ddp = 0;
695               id = (id & 0x3f) | ((id >> 1) & ~0x3f);
696               /* domatch */
697               while (md->stop == SEARCH_NEXT_KEY && ddp)
698                 ddp = data_read_id(ddp, &id);
699               break;
700             default:
701               ddp = 0;
702             }
703         }
704       if (onekey || md->stop > SEARCH_NEXT_KEY)
705         return;
706     }
707 }
708
709 static void
710 domatch_idarray(Id p, Id key, struct matchdata *md, Id *ida)
711 {
712   for (; *ida && !md->stop; ida++)
713     domatch(p, key, md, id2str(md->pool, *ida));
714 }
715
716 static void
717 repo_search_md(Repo *repo, Id p, Id key, struct matchdata *md)
718 {
719   Pool *pool = repo->pool;
720   Repodata *data;
721   Solvable *s;
722   int i;
723
724   md->stop = 0;
725   if (!p)
726     {
727       for (p = repo->start, s = repo->pool->solvables + p; p < repo->end; p++, s++)
728         {
729           if (s->repo == repo)
730             repo_search_md(repo, p, key, md);
731           if (md->stop > SEARCH_NEXT_SOLVABLE)
732             break;
733         }
734       return;
735     }
736   s = pool->solvables + p;
737   switch(key)
738     {
739       case 0:
740       case SOLVABLE_NAME:
741         if (s->name)
742           domatch(p, SOLVABLE_NAME, md, id2str(pool, s->name));
743         if (key || md->stop > SEARCH_NEXT_KEY)
744           return;
745       case SOLVABLE_ARCH:
746         if (s->arch)
747           domatch(p, SOLVABLE_ARCH, md, id2str(pool, s->arch));
748         if (key || md->stop > SEARCH_NEXT_KEY)
749           return;
750       case SOLVABLE_EVR:
751         if (s->evr)
752           domatch(p, SOLVABLE_EVR, md, id2str(pool, s->evr));
753         if (key || md->stop > SEARCH_NEXT_KEY)
754           return;
755       case SOLVABLE_VENDOR:
756         if (s->vendor)
757           domatch(p, SOLVABLE_VENDOR, md, id2str(pool, s->vendor));
758         if (key || md->stop > SEARCH_NEXT_KEY)
759           return;
760       case SOLVABLE_PROVIDES:
761         if (s->provides)
762           domatch_idarray(p, SOLVABLE_PROVIDES, md, repo->idarraydata + s->provides);
763         if (key || md->stop > SEARCH_NEXT_KEY)
764           return;
765       case SOLVABLE_OBSOLETES:
766         if (s->obsoletes)
767           domatch_idarray(p, SOLVABLE_OBSOLETES, md, repo->idarraydata + s->obsoletes);
768         if (key || md->stop > SEARCH_NEXT_KEY)
769           return;
770       case SOLVABLE_CONFLICTS:
771         if (s->obsoletes)
772           domatch_idarray(p, SOLVABLE_CONFLICTS, md, repo->idarraydata + s->conflicts);
773         if (key || md->stop > SEARCH_NEXT_KEY)
774           return;
775       case SOLVABLE_REQUIRES:
776         if (s->requires)
777           domatch_idarray(p, SOLVABLE_REQUIRES, md, repo->idarraydata + s->requires);
778         if (key || md->stop > SEARCH_NEXT_KEY)
779           return;
780       case SOLVABLE_RECOMMENDS:
781         if (s->recommends)
782           domatch_idarray(p, SOLVABLE_RECOMMENDS, md, repo->idarraydata + s->recommends);
783         if (key || md->stop > SEARCH_NEXT_KEY)
784           return;
785       case SOLVABLE_SUPPLEMENTS:
786         if (s->supplements)
787           domatch_idarray(p, SOLVABLE_SUPPLEMENTS, md, repo->idarraydata + s->supplements);
788         if (key || md->stop > SEARCH_NEXT_KEY)
789           return;
790       case SOLVABLE_SUGGESTS:
791         if (s->suggests)
792           domatch_idarray(p, SOLVABLE_SUGGESTS, md, repo->idarraydata + s->suggests);
793         if (key || md->stop > SEARCH_NEXT_KEY)
794           return;
795       case SOLVABLE_ENHANCES:
796         if (s->enhances)
797           domatch_idarray(p, SOLVABLE_ENHANCES, md, repo->idarraydata + s->enhances);
798         if (key || md->stop > SEARCH_NEXT_KEY)
799           return;
800       case SOLVABLE_FRESHENS:
801         if (s->freshens)
802           domatch_idarray(p, SOLVABLE_FRESHENS, md, repo->idarraydata + s->freshens);
803         if (key || md->stop > SEARCH_NEXT_KEY)
804           return;
805       default:
806         break;
807     }
808
809   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
810     {
811       if (p < data->start || p >= data->end)
812         continue;
813       repodata_search(data, p - data->start, key, md);
814       if (md->stop > SEARCH_NEXT_KEY)
815         break;
816     }
817 }
818
819 void
820 repo_search(Repo *repo, Id p, Id key, const char *match, int flags, int (*callback)(void *data, Solvable *s, Id key, const char *str), void *callback_data)
821 {
822   struct matchdata md;
823
824   memset(&md, 0, sizeof(md));
825   md.pool = repo->pool;
826   md.matchstr = match;
827   md.flags = flags;
828   md.callback = callback;
829   md.callback_data = callback_data;
830   repo_search_md(repo, p, key, &md);
831 }
832
833 const char *
834 repo_lookup_str(Solvable *s, Id key)
835 {
836   Repo *repo = s->repo;
837   Pool *pool = repo->pool;
838   Repodata *data;
839   int i, j, n;
840
841   switch(key)
842     {
843     case SOLVABLE_NAME:
844       return id2str(pool, s->name);
845     case SOLVABLE_ARCH:
846       return id2str(pool, s->arch);
847     case SOLVABLE_EVR:
848       return id2str(pool, s->evr);
849     case SOLVABLE_VENDOR:
850       return id2str(pool, s->vendor);
851     }
852   n = s - pool->solvables;
853   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
854     {
855       if (n < data->start || n >= data->end)
856         continue;
857       for (j = 1; j < data->nkeys; j++)
858         {
859           if (data->keys[j].name == key && (data->keys[j].type == TYPE_ID || data->keys[j].type == TYPE_STR))
860             return repodata_lookup_str(data, n - data->start, j);
861         }
862     }
863   return 0;
864 }
865
866
867
868 static int
869 key_cmp (const void *pa, const void *pb)
870 {
871   Repokey *a = (Repokey *)pa;
872   Repokey *b = (Repokey *)pb;
873   return a->name - b->name;
874 }
875
876 void
877 repo_add_attrstore (Repo *repo, Attrstore *s, const char *location)
878 {
879   unsigned i;
880   Repodata *data;
881   /* If this is meant to be the embedded attributes, make sure we don't
882      have them already.  */
883   if (!location)
884     {
885       for (i = 0; i < repo->nrepodata; i++)
886         if (repo->repodata[i].location == 0)
887           break;
888       if (i != repo->nrepodata)
889         {
890           pool_debug (repo->pool, SAT_FATAL, "embedded attribs added twice\n");
891           exit (1);
892         }
893     }
894   repo->nrepodata++;
895   repo->repodata = xrealloc (repo->repodata, repo->nrepodata * sizeof (*data));
896   data = repo->repodata + repo->nrepodata - 1;
897   memset (data, 0, sizeof (*data));
898   data->s = s;
899   data->nkeys = s->nkeys;
900   if (data->nkeys)
901     {
902       data->keys = xmalloc(data->nkeys * sizeof(data->keys[0]));
903       for (i = 1; i < data->nkeys; i++)
904         {
905           data->keys[i].name = s->keys[i].name;
906           data->keys[i].type = s->keys[i].type;
907         }
908       if (data->nkeys > 2)
909         qsort(data->keys + 1, data->nkeys - 1, sizeof(data->keys[0]), key_cmp);
910     }
911   if (location)
912     data->location = strdup(location);
913 }
914
915 // EOF