- get missing translations from other solvables
[platform/upstream/libsolv.git] / src / repodata.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  * repodata.c
10  *
11  * Manage data coming from one repository
12  *
13  * a repository can contain multiple repodata entries, consisting of
14  * different sets of keys and different sets of solvables
15  */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <fnmatch.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <regex.h>
26
27 #include "repo.h"
28 #include "pool.h"
29 #include "poolid_private.h"
30 #include "util.h"
31 #include "hash.h"
32 #include "chksum.h"
33
34 #include "repopack.h"
35 #include "repopage.h"
36
37 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
38                                   unsigned char *out, unsigned int out_len);
39 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
40                                               unsigned int in_len,
41                                               unsigned char *out,
42                                               unsigned int out_len);
43
44 #define REPODATA_BLOCK 255
45
46
47 void
48 repodata_initdata(Repodata *data, Repo *repo, int localpool)
49 {
50   memset(data, 0, sizeof (*data));
51   data->repo = repo;
52   data->localpool = localpool;
53   if (localpool)
54     stringpool_init_empty(&data->spool);
55   data->keys = sat_calloc(1, sizeof(Repokey));
56   data->nkeys = 1;
57   data->schemata = sat_calloc(1, sizeof(Id));
58   data->schemadata = sat_calloc(1, sizeof(Id));
59   data->nschemata = 1;
60   data->schemadatalen = 1;
61   repopagestore_init(&data->store);
62 }
63
64 void
65 repodata_freedata(Repodata *data)
66 {
67   int i;
68
69   sat_free(data->keys);
70
71   sat_free(data->schemata);
72   sat_free(data->schemadata);
73   sat_free(data->schematahash);
74
75   stringpool_free(&data->spool);
76   dirpool_free(&data->dirpool);
77
78   sat_free(data->mainschemaoffsets);
79   sat_free(data->incoredata);
80   sat_free(data->incoreoffset);
81   sat_free(data->verticaloffset);
82
83   repopagestore_free(&data->store);
84
85   sat_free(data->vincore);
86
87   if (data->attrs)
88     for (i = 0; i < data->end - data->start; i++)
89       sat_free(data->attrs[i]);
90   sat_free(data->attrs);
91   if (data->xattrs)
92     for (i = 0; i < data->nxattrs; i++)
93       sat_free(data->xattrs[i]);
94   sat_free(data->xattrs);
95
96   sat_free(data->attrdata);
97   sat_free(data->attriddata);
98 }
99
100 Repodata *
101 repodata_create(Repo *repo, int localpool)
102 {
103   Repodata *data;
104
105   repo->nrepodata++;
106   repo->repodata = sat_realloc2(repo->repodata, repo->nrepodata, sizeof(*data));
107   data = repo->repodata + repo->nrepodata - 1;
108   repodata_initdata(data, repo, localpool);
109   return data;
110 }
111
112 void
113 repodata_free(Repodata *data)
114 {
115   Repo *repo = data->repo;
116   int i = data - repo->repodata;
117   repodata_freedata(data);
118   if (i < repo->nrepodata - 1)
119     memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
120   repo->nrepodata--;
121 }
122
123
124 /***************************************************************
125  * key pool management
126  */
127
128 /* this is not so time critical that we need a hash, so we do a simple
129  * linear search */
130 Id
131 repodata_key2id(Repodata *data, Repokey *key, int create)
132 {
133   Id keyid;
134
135   for (keyid = 1; keyid < data->nkeys; keyid++)
136     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
137       {
138         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
139           continue;
140         break;
141       }
142   if (keyid == data->nkeys)
143     {
144       if (!create)
145         return 0;
146       /* allocate new key */
147       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
148       data->keys[data->nkeys++] = *key;
149       if (data->verticaloffset)
150         {
151           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
152           data->verticaloffset[data->nkeys - 1] = 0;
153         }
154       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
155     }
156   return keyid;
157 }
158
159
160 /***************************************************************
161  * schema pool management
162  */
163
164 #define SCHEMATA_BLOCK 31
165 #define SCHEMATADATA_BLOCK 255
166
167 Id
168 repodata_schema2id(Repodata *data, Id *schema, int create)
169 {
170   int h, len, i;
171   Id *sp, cid;
172   Id *schematahash;
173
174   if ((schematahash = data->schematahash) == 0)
175     {
176       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
177       for (i = 0; i < data->nschemata; i++)
178         {
179           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
180             h = h * 7 + *sp++;
181           h &= 255;
182           schematahash[h] = i + 1;
183         }
184       data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
185       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
186     }
187
188   for (sp = schema, len = 0, h = 0; *sp; len++)
189     h = h * 7 + *sp++;
190   h &= 255;
191   len++;
192
193   cid = schematahash[h];
194   if (cid)
195     {
196       cid--;
197       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
198         return cid;
199       /* cache conflict */
200       for (cid = 0; cid < data->nschemata; cid++)
201         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
202           return cid;
203     }
204   /* a new one */
205   if (!create)
206     return 0;
207   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
208   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
209   /* add schema */
210   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
211   data->schemata[data->nschemata] = data->schemadatalen;
212   data->schemadatalen += len;
213   schematahash[h] = data->nschemata + 1;
214 #if 0
215 fprintf(stderr, "schema2id: new schema\n");
216 #endif
217   return data->nschemata++;
218 }
219
220 void
221 repodata_free_schemahash(Repodata *data)
222 {
223   data->schematahash = sat_free(data->schematahash);
224   /* shrink arrays */
225   data->schemata = sat_realloc2(data->schemata, data->nschemata, sizeof(Id));
226   data->schemadata = sat_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
227 }
228
229
230 /***************************************************************
231  * dir pool management
232  */
233
234 Id
235 repodata_str2dir(Repodata *data, const char *dir, int create)
236 {
237   Id id, parent;
238   const char *dire;
239
240   parent = 0;
241   while (*dir == '/' && dir[1] == '/')
242     dir++;
243   if (*dir == '/' && !dir[1])
244     {
245       if (data->dirpool.ndirs)
246         return 1;
247       return dirpool_add_dir(&data->dirpool, 0, 1, create);
248     }
249   while (*dir)
250     {
251       dire = strchrnul(dir, '/');
252       if (data->localpool)
253         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
254       else
255         id = strn2id(data->repo->pool, dir, dire - dir, create);
256       if (!id)
257         return 0;
258       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
259       if (!parent)
260         return 0;
261       if (!*dire)
262         break;
263       dir = dire + 1;
264       while (*dir == '/')
265         dir++;
266     }
267   return parent;
268 }
269
270 const char *
271 repodata_dir2str(Repodata *data, Id did, const char *suf)
272 {
273   Pool *pool = data->repo->pool;
274   int l = 0;
275   Id parent, comp;
276   const char *comps;
277   char *p;
278
279   if (!did)
280     return suf ? suf : "";
281   parent = did;
282   while (parent)
283     {
284       comp = dirpool_compid(&data->dirpool, parent);
285       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
286       l += strlen(comps);
287       parent = dirpool_parent(&data->dirpool, parent);
288       if (parent)
289         l++;
290     }
291   if (suf)
292     l += strlen(suf) + 1;
293   p = pool_alloctmpspace(pool, l + 1) + l;
294   *p = 0;
295   if (suf)
296     {
297       p -= strlen(suf);
298       strcpy(p, suf);
299       *--p = '/';
300     }
301   parent = did;
302   while (parent)
303     {
304       comp = dirpool_compid(&data->dirpool, parent);
305       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
306       l = strlen(comps);
307       p -= l;
308       strncpy(p, comps, l);
309       parent = dirpool_parent(&data->dirpool, parent);
310       if (parent)
311         *--p = '/';
312     }
313   return p;
314 }
315
316
317 /***************************************************************
318  * data management
319  */
320
321 static inline unsigned char *
322 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
323 {
324   Id *keyp = data->schemadata + data->schemata[schema];
325   for (; *keyp; keyp++)
326     dp = data_skip_key(data, dp, data->keys + *keyp);
327   return dp;
328 }
329
330 unsigned char *
331 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
332 {
333   int nentries, schema;
334   switch(key->type)
335     {
336     case REPOKEY_TYPE_FIXARRAY:
337       dp = data_read_id(dp, &nentries);
338       if (!nentries)
339         return dp;
340       dp = data_read_id(dp, &schema);
341       while (nentries--)
342         dp = data_skip_schema(data, dp, schema);
343       return dp;
344     case REPOKEY_TYPE_FLEXARRAY:
345       dp = data_read_id(dp, &nentries);
346       while (nentries--)
347         {
348           dp = data_read_id(dp, &schema);
349           dp = data_skip_schema(data, dp, schema);
350         }
351       return dp;
352     default:
353       if (key->storage == KEY_STORAGE_INCORE)
354         dp = data_skip(dp, key->type);
355       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
356         {
357           dp = data_skip(dp, REPOKEY_TYPE_ID);
358           dp = data_skip(dp, REPOKEY_TYPE_ID);
359         }
360       return dp;
361     }
362 }
363
364 static unsigned char *
365 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
366 {
367   Id k;
368
369   if (!keyid)
370     return 0;
371   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
372     {
373       int i;
374       for (i = 0; (k = *keyp++) != 0; i++)
375         if (k == keyid)
376           return data->incoredata + data->mainschemaoffsets[i];
377       return 0;
378     }
379   while ((k = *keyp++) != 0)
380     {
381       if (k == keyid)
382         return dp;
383       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
384         {
385           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
386           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
387           continue;
388         }
389       if (data->keys[k].storage != KEY_STORAGE_INCORE)
390         continue;
391       dp = data_skip_key(data, dp, data->keys + k);
392     }
393   return 0;
394 }
395
396 static unsigned char *
397 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
398 {
399   unsigned char *dp;
400   if (!len)
401     return 0;
402   if (off >= data->lastverticaloffset)
403     {
404       off -= data->lastverticaloffset;
405       if (off + len > data->vincorelen)
406         return 0;
407       return data->vincore + off;
408     }
409   if (off + len > key->size)
410     return 0;
411   /* we now have the offset, go into vertical */
412   off += data->verticaloffset[key - data->keys];
413   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
414   dp = repopagestore_load_page_range(&data->store, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
415   if (dp)
416     dp += off % BLOB_PAGESIZE;
417   return dp;
418 }
419
420 static inline unsigned char *
421 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
422 {
423   unsigned char *dp = *dpp;
424
425   if (!dp)
426     return 0;
427   if (key->storage == KEY_STORAGE_INCORE)
428     {
429       if (advance)
430         *dpp = data_skip_key(data, dp, key);
431       return dp;
432     }
433   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
434     {
435       Id off, len;
436       dp = data_read_id(dp, &off);
437       dp = data_read_id(dp, &len);
438       if (advance)
439         *dpp = dp;
440       return get_vertical_data(data, key, off, len);
441     }
442   return 0;
443 }
444
445 static int
446 load_repodata(Repodata *data)
447 {
448   if (data->loadcallback)
449     {
450       data->loadcallback(data);
451       if (data->state == REPODATA_AVAILABLE)
452         return 1;
453     }
454   data->state = REPODATA_ERROR;
455   return 0;
456 }
457
458 static inline int
459 maybe_load_repodata(Repodata *data, Id keyname)
460 {
461   if (keyname && !repodata_precheck_keyname(data, keyname))
462     return 0;   /* do not bother... */
463   switch(data->state)
464     {
465     case REPODATA_STUB:
466       if (keyname)
467         {
468           int i;
469           for (i = 0; i < data->nkeys; i++)
470             if (keyname == data->keys[i].name)
471               break;
472           if (i == data->nkeys)
473             return 0;
474         }
475       return load_repodata(data);
476     case REPODATA_ERROR:
477       return 0;
478     case REPODATA_AVAILABLE:
479     case REPODATA_LOADING:
480       return 1;
481     default:
482       data->state = REPODATA_ERROR;
483       return 0;
484     }
485 }
486
487 static inline unsigned char *
488 solvid2data(Repodata *data, Id solvid, Id *schemap)
489 {
490   unsigned char *dp = data->incoredata;
491   if (!dp)
492     return 0;
493   if (solvid == SOLVID_META)    /* META */
494     dp += 1;
495   else if (solvid == SOLVID_POS)        /* META */
496     {
497       Pool *pool = data->repo->pool;
498       if (data->repo != pool->pos.repo)
499         return 0;
500       if (data != data->repo->repodata + pool->pos.repodataid)
501         return 0;
502       *schemap = pool->pos.schema;
503       return data->incoredata + pool->pos.dp;
504     }
505   else
506     {
507       if (solvid < data->start || solvid >= data->end)
508         return 0;
509       dp += data->incoreoffset[solvid - data->start];
510     }
511   return data_read_id(dp, schemap);
512 }
513
514 /************************************************************************
515  * data lookup
516  */
517
518 static inline unsigned char *
519 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
520 {
521   unsigned char *dp;
522   Id schema, *keyp, *kp;
523   Repokey *key;
524
525   if (!maybe_load_repodata(data, keyname))
526     return 0;
527   dp = solvid2data(data, solvid, &schema);
528   if (!dp)
529     return 0;
530   keyp = data->schemadata + data->schemata[schema];
531   for (kp = keyp; *kp; kp++)
532     if (data->keys[*kp].name == keyname)
533       break;
534   if (!*kp)
535     return 0;
536   *keypp = key = data->keys + *kp;
537   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
538     return dp;  /* no need to forward... */
539   dp = forward_to_key(data, *kp, keyp, dp);
540   if (!dp)
541     return 0;
542   return get_data(data, key, &dp, 0);
543 }
544
545
546 Id
547 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
548 {
549   unsigned char *dp;
550   Repokey *key;
551   Id id;
552
553   dp = find_key_data(data, solvid, keyname, &key);
554   if (!dp)
555     return 0;
556   if (key->type == REPOKEY_TYPE_CONSTANTID)
557     return key->size;
558   if (key->type != REPOKEY_TYPE_ID)
559     return 0;
560   dp = data_read_id(dp, &id);
561   return id;
562 }
563
564 Id
565 repodata_globalize_id(Repodata *data, Id id, int create)
566 {
567   if (!id || !data || !data->localpool)
568     return id;
569   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
570 }
571
572 const char *
573 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
574 {
575   unsigned char *dp;
576   Repokey *key;
577   Id id;
578
579   dp = find_key_data(data, solvid, keyname, &key);
580   if (!dp)
581     return 0;
582   if (key->type == REPOKEY_TYPE_STR)
583     return (const char *)dp;
584   if (key->type == REPOKEY_TYPE_CONSTANTID)
585     return id2str(data->repo->pool, key->size);
586   if (key->type == REPOKEY_TYPE_ID)
587     dp = data_read_id(dp, &id);
588   else
589     return 0;
590   if (data->localpool)
591     return data->spool.stringspace + data->spool.strings[id];
592   return id2str(data->repo->pool, id);
593 }
594
595 int
596 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned int *value)
597 {
598   unsigned char *dp;
599   Repokey *key;
600   KeyValue kv;
601
602   *value = 0;
603   dp = find_key_data(data, solvid, keyname, &key);
604   if (!dp)
605     return 0;
606   if (key->type == REPOKEY_TYPE_NUM
607       || key->type == REPOKEY_TYPE_U32
608       || key->type == REPOKEY_TYPE_CONSTANT)
609     {
610       dp = data_fetch(dp, &kv, key);
611       *value = kv.num;
612       return 1;
613     }
614   return 0;
615 }
616
617 int
618 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
619 {
620   Id schema;
621   Id *keyp;
622   unsigned char *dp;
623
624   if (!maybe_load_repodata(data, keyname))
625     return 0;
626   dp = solvid2data(data, solvid, &schema);
627   if (!dp)
628     return 0;
629   /* can't use find_key_data as we need to test the type */
630   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
631     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
632       return 1;
633   return 0;
634 }
635
636 const unsigned char *
637 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
638 {
639   unsigned char *dp;
640   Repokey *key;
641
642   dp = find_key_data(data, solvid, keyname, &key);
643   if (!dp)
644     return 0;
645   *typep = key->type;
646   return dp;
647 }
648
649 int
650 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
651 {
652   unsigned char *dp;
653   Repokey *key;
654   Id id;
655   int eof = 0;
656
657   queue_empty(q);
658   dp = find_key_data(data, solvid, keyname, &key);
659   if (!dp)
660     return 0;
661   for (;;)
662     {
663       dp = data_read_ideof(dp, &id, &eof);
664       queue_push(q, id);
665       if (eof)
666         break;
667     }
668   return 1;
669 }
670
671 /************************************************************************
672  * data search
673  */
674
675
676 int
677 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
678 {
679   switch (key->type)
680     {
681     case REPOKEY_TYPE_ID:
682     case REPOKEY_TYPE_CONSTANTID:
683     case REPOKEY_TYPE_IDARRAY:
684       if (data && data->localpool)
685         kv->str = stringpool_id2str(&data->spool, kv->id);
686       else
687         kv->str = id2str(pool, kv->id);
688       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
689         {
690           const char *s;
691           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
692             ;
693           if (*s == ':' && s > kv->str)
694             kv->str = s + 1;
695         }
696       return 1;
697     case REPOKEY_TYPE_STR:
698       return 1;
699     case REPOKEY_TYPE_DIRSTRARRAY:
700       if (!(flags & SEARCH_FILES))
701         return 1;       /* match just the basename */
702       /* Put the full filename into kv->str.  */
703       kv->str = repodata_dir2str(data, kv->id, kv->str);
704       /* And to compensate for that put the "empty" directory into
705          kv->id, so that later calls to repodata_dir2str on this data
706          come up with the same filename again.  */
707       kv->id = 0;
708       return 1;
709     case REPOKEY_TYPE_MD5:
710     case REPOKEY_TYPE_SHA1:
711     case REPOKEY_TYPE_SHA256:
712       if (!(flags & SEARCH_CHECKSUMS))
713         return 0;       /* skip em */
714       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
715       return 1;
716     default:
717       return 0;
718     }
719 }
720
721
722 struct subschema_data {
723   Solvable *s;
724   void *cbdata;
725   KeyValue *parent;
726 };
727
728 /* search a specific repodata */
729 void
730 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
731 {
732   Id schema;
733   Repokey *key;
734   Id keyid, *kp, *keyp;
735   unsigned char *dp, *ddp;
736   int onekey = 0;
737   int stop;
738   KeyValue kv;
739   Solvable *s;
740
741   if (!maybe_load_repodata(data, keyname))
742     return;
743   if (solvid == SOLVID_SUBSCHEMA)
744     {
745       struct subschema_data *subd = cbdata;
746       cbdata = subd->cbdata;
747       s = subd->s;
748       schema = subd->parent->id;
749       dp = (unsigned char *)subd->parent->str;
750       kv.parent = subd->parent;
751     }
752   else
753     {
754       schema = 0;
755       dp = solvid2data(data, solvid, &schema);
756       if (!dp)
757         return;
758       s = data->repo->pool->solvables + solvid;
759       kv.parent = 0;
760     }
761   keyp = data->schemadata + data->schemata[schema];
762   if (keyname)
763     {
764       /* search for a specific key */
765       for (kp = keyp; *kp; kp++)
766         if (data->keys[*kp].name == keyname)
767           break;
768       if (!*kp)
769         return;
770       dp = forward_to_key(data, *kp, keyp, dp);
771       if (!dp)
772         return;
773       keyp = kp;
774       onekey = 1;
775     }
776   while ((keyid = *keyp++) != 0)
777     {
778       stop = 0;
779       key = data->keys + keyid;
780       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
781
782       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
783         {
784           struct subschema_data subd;
785           int nentries;
786           Id schema = 0;
787
788           subd.cbdata = cbdata;
789           subd.s = s;
790           subd.parent = &kv;
791           ddp = data_read_id(ddp, &nentries);
792           kv.num = nentries;
793           kv.entry = 0;
794           kv.eof = 0;
795           while (ddp && nentries > 0)
796             {
797               if (!--nentries)
798                 kv.eof = 1;
799               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
800                 ddp = data_read_id(ddp, &schema);
801               kv.id = schema;
802               kv.str = (char *)ddp;
803               stop = callback(cbdata, s, data, key, &kv);
804               if (stop > SEARCH_NEXT_KEY)
805                 return;
806               if (stop && stop != SEARCH_ENTERSUB)
807                 break;
808               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
809                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
810               ddp = data_skip_schema(data, ddp, schema);
811               kv.entry++;
812             }
813           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
814             {
815               /* sentinel */
816               kv.eof = 2;
817               kv.str = (char *)ddp;
818               stop = callback(cbdata, s, data, key, &kv);
819               if (stop > SEARCH_NEXT_KEY)
820                 return;
821             }
822           if (onekey)
823             return;
824           continue;
825         }
826       kv.entry = 0;
827       do
828         {
829           ddp = data_fetch(ddp, &kv, key);
830           if (!ddp)
831             break;
832           stop = callback(cbdata, s, data, key, &kv);
833           kv.entry++;
834         }
835       while (!kv.eof && !stop);
836       if (onekey || stop > SEARCH_NEXT_KEY)
837         return;
838     }
839 }
840
841 void
842 repodata_setpos_kv(Repodata *data, KeyValue *kv)
843 {
844   Pool *pool = data->repo->pool;
845   if (!kv)
846     pool_clear_pos(pool);
847   else
848     {
849       pool->pos.repo = data->repo;
850       pool->pos.repodataid = data - data->repo->repodata;
851       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
852       pool->pos.schema = kv->id;
853     }
854 }
855
856 /************************************************************************
857  * data iterator functions
858  */
859
860 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
861   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
862   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
863   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
864   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
865   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
866   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
867   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
868   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
869   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
870   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
871   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
872   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
873   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
874 };
875
876 static inline Id *
877 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
878 {
879   kv->id = keyname;
880   switch (keyname)
881     {
882     case SOLVABLE_NAME:
883       kv->eof = 1;
884       return &s->name;
885     case SOLVABLE_ARCH:
886       kv->eof = 1;
887       return &s->arch;
888     case SOLVABLE_EVR:
889       kv->eof = 1;
890       return &s->evr;
891     case SOLVABLE_VENDOR:
892       kv->eof = 1;
893       return &s->vendor;
894     case SOLVABLE_PROVIDES:
895       kv->eof = 0;
896       return s->provides ? s->repo->idarraydata + s->provides : 0;
897     case SOLVABLE_OBSOLETES:
898       kv->eof = 0;
899       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
900     case SOLVABLE_CONFLICTS:
901       kv->eof = 0;
902       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
903     case SOLVABLE_REQUIRES:
904       kv->eof = 0;
905       return s->requires ? s->repo->idarraydata + s->requires : 0;
906     case SOLVABLE_RECOMMENDS:
907       kv->eof = 0;
908       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
909     case SOLVABLE_SUPPLEMENTS:
910       kv->eof = 0;
911       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
912     case SOLVABLE_SUGGESTS:
913       kv->eof = 0;
914       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
915     case SOLVABLE_ENHANCES:
916       kv->eof = 0;
917       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
918     case RPM_RPMDBID:
919       kv->eof = 1;
920       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
921     default:
922       return 0;
923     }
924 }
925
926 int
927 datamatcher_init(Datamatcher *ma, const char *match, int flags)
928 {
929   ma->match = match;
930   ma->flags = flags;
931   ma->error = 0;
932   ma->matchdata = 0;
933   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
934     {
935       ma->matchdata = sat_calloc(1, sizeof(regex_t));
936       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
937       if (ma->error)
938         {
939           sat_free(ma->matchdata);
940           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
941         }
942     }
943   return ma->error;
944 }
945
946 void
947 datamatcher_free(Datamatcher *ma)
948 {
949   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
950     {
951       regfree(ma->matchdata);
952       ma->matchdata = sat_free(ma->matchdata);
953     }
954 }
955
956 int
957 datamatcher_match(Datamatcher *ma, const char *str)
958 {
959   int l;
960   switch ((ma->flags & SEARCH_STRINGMASK))
961     {
962     case SEARCH_SUBSTRING:
963       if (ma->flags & SEARCH_NOCASE)
964         {
965           if (!strcasestr(str, ma->match))
966             return 0;
967         }
968       else
969         {
970           if (!strstr(str, ma->match))
971             return 0;
972         }
973       break;
974     case SEARCH_STRING:
975       if (ma->flags & SEARCH_NOCASE)
976         {
977           if (strcasecmp(ma->match, str))
978             return 0;
979         }
980       else
981         {
982           if (strcmp(ma->match, str))
983             return 0;
984         }
985       break;
986     case SEARCH_STRINGSTART:
987       if (ma->flags & SEARCH_NOCASE)
988         {
989           if (strncasecmp(ma->match, str, strlen(ma->match)))
990             return 0;
991         }
992       else
993         {
994           if (strncmp(ma->match, str, strlen(ma->match)))
995             return 0;
996         }
997       break;
998     case SEARCH_STRINGEND:
999       l = strlen(str) - strlen(ma->match);
1000       if (l < 0)
1001         return 0;
1002       if (ma->flags & SEARCH_NOCASE)
1003         {
1004           if (strcasecmp(ma->match, str + l))
1005             return 0;
1006         }
1007       else
1008         {
1009           if (strcmp(ma->match, str + l))
1010             return 0;
1011         }
1012       break;
1013     case SEARCH_GLOB:
1014       if (fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1015         return 0;
1016       break;
1017     case SEARCH_REGEX:
1018       if (regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0))
1019         return 0;
1020       break;
1021     default:
1022       return 0;
1023     }
1024   return 1;
1025 }
1026
1027 int
1028 repodata_filelistfilter_matches(Repodata *data, const char *str)
1029 {
1030   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1031   /* for now hardcoded */
1032   if (strstr(str, "bin/"))
1033     return 1;
1034   if (!strncmp(str, "/etc/", 5))
1035     return 1;
1036   if (!strcmp(str, "/usr/lib/sendmail"))
1037     return 1;
1038   return 0;
1039 }
1040
1041
1042 enum {
1043   di_bye,
1044
1045   di_enterrepo,
1046   di_entersolvable,
1047   di_enterrepodata,
1048   di_enterschema,
1049   di_enterkey,
1050
1051   di_nextattr,
1052   di_nextkey,
1053   di_nextrepodata,
1054   di_nextsolvable,
1055   di_nextrepo,
1056
1057   di_enterarray,
1058   di_nextarrayelement,
1059
1060   di_entersub,
1061   di_leavesub,
1062
1063   di_nextsolvableattr,
1064   di_nextsolvablekey,
1065   di_entersolvablekey
1066 };
1067
1068 /* see repo.h for documentation */
1069 int
1070 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1071 {
1072   memset(di, 0, sizeof(*di));
1073   di->pool = pool;
1074   di->flags = flags & ~SEARCH_THISSOLVID;
1075   if (!pool || (repo && repo->pool != pool))
1076     {
1077       di->state = di_bye;
1078       return -1;
1079     }
1080   if (match)
1081     {
1082       int error;
1083       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1084         {
1085           di->state = di_bye;
1086           return error;
1087         }
1088     }
1089   di->keyname = keyname;
1090   di->keynames[0] = keyname;
1091   dataiterator_set_search(di, repo, p);
1092   return 0;
1093 }
1094
1095 void
1096 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1097 {
1098   *di = *from;
1099   memset(&di->matcher, 0, sizeof(di->matcher));
1100   if (from->matcher.match)
1101     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1102   if (di->nparents)
1103     {
1104       /* fix pointers */
1105       int i;
1106       for (i = 1; i < di->nparents; i++)
1107         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1108       di->kv.parent = &di->parents[di->nparents - 1].kv;
1109     }
1110 }
1111
1112 int
1113 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1114 {
1115   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1116   datamatcher_free(&di->matcher);
1117   memset(&di->matcher, 0, sizeof(di->matcher));
1118   if (match)
1119     {
1120       int error;
1121       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1122         {
1123           di->state = di_bye;
1124           return error;
1125         }
1126     }
1127   return 0;
1128 }
1129
1130 void
1131 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1132 {
1133   di->repo = repo;
1134   di->repoid = -1;
1135   di->flags &= ~SEARCH_THISSOLVID;
1136   di->nparents = 0;
1137   di->rootlevel = 0;
1138   di->repodataid = 0;
1139   if (!di->pool->nrepos)
1140     {
1141       di->state = di_bye;
1142       return;
1143     }
1144   if (!repo)
1145     {
1146       di->repoid = 0;
1147       di->repo = di->pool->repos[0];
1148     }
1149   di->state = di_enterrepo;
1150   if (p)
1151     dataiterator_jump_to_solvid(di, p);
1152 }
1153
1154 void
1155 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1156 {
1157   di->nkeynames = 0;
1158   di->keyname = keyname;
1159   di->keynames[0] = keyname;
1160 }
1161
1162 void
1163 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1164 {
1165   int i;
1166
1167   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1168     {
1169       di->state = di_bye;       /* sorry */
1170       return;
1171     }
1172   for (i = di->nkeynames + 1; i > 0; i--)
1173     di->keynames[i] = di->keynames[i - 1];
1174   di->keynames[0] = di->keyname = keyname;
1175   di->nkeynames++;
1176 }
1177
1178 void
1179 dataiterator_free(Dataiterator *di)
1180 {
1181   if (di->matcher.match)
1182     datamatcher_free(&di->matcher);
1183 }
1184
1185 static inline unsigned char *
1186 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1187 {
1188   Id *keyp = di->keyp;
1189   Repokey *keys = di->data->keys;
1190   unsigned char *dp;
1191
1192   for (keyp = di->keyp; *keyp; keyp++)
1193     if (keys[*keyp].name == keyname)
1194       break;
1195   if (!*keyp)
1196     return 0;
1197   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1198   if (!dp)
1199     return 0;
1200   di->keyp = keyp;
1201   return dp;
1202 }
1203
1204 static int
1205 dataiterator_filelistcheck(Dataiterator *di)
1206 {
1207   int j;
1208   int needcomplete = 0;
1209   Repodata *data = di->data;
1210
1211   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
1212     if (!di->matcher.match || (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING || !repodata_filelistfilter_matches(di->data, di->matcher.match))
1213       needcomplete = 1;
1214   if (data->state != REPODATA_AVAILABLE)
1215     return needcomplete ? 1 : 0;
1216   for (j = 1; j < data->nkeys; j++)
1217     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1218       break;
1219   return j == data->nkeys && !needcomplete ? 0 : 1;
1220 }
1221
1222 int
1223 dataiterator_step(Dataiterator *di)
1224 {
1225   Id schema;
1226
1227   for (;;)
1228     {
1229       switch (di->state)
1230         {
1231         case di_enterrepo: di_enterrepo:
1232           if (!di->repo)
1233             goto di_bye;
1234           if (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS))
1235             goto di_nextrepo;
1236           if (!(di->flags & SEARCH_THISSOLVID))
1237             {
1238               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1239               goto di_nextsolvable;
1240             }
1241           /* FALLTHROUGH */
1242
1243         case di_entersolvable: di_entersolvable:
1244           if (di->repodataid >= 0)
1245             {
1246               di->repodataid = 0;       /* reset repodata iterator */
1247               if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
1248                 {
1249                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1250                   di->data = 0;
1251                   goto di_entersolvablekey;
1252                 }
1253             }
1254           /* FALLTHROUGH */
1255
1256         case di_enterrepodata: di_enterrepodata:
1257           if (di->repodataid >= 0)
1258             {
1259               if (di->repodataid >= di->repo->nrepodata)
1260                 goto di_nextsolvable;
1261               di->data = di->repo->repodata + di->repodataid;
1262             }
1263           if (di->repodataid >= 0 && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1264             goto di_nextrepodata;
1265           if (!maybe_load_repodata(di->data, di->keyname))
1266             goto di_nextrepodata;
1267           di->dp = solvid2data(di->data, di->solvid, &schema);
1268           if (!di->dp)
1269             goto di_nextrepodata;
1270           if (di->solvid == SOLVID_POS)
1271             di->solvid = di->pool->pos.solvid;
1272           /* reset key iterator */
1273           di->keyp = di->data->schemadata + di->data->schemata[schema];
1274           /* FALLTHROUGH */
1275
1276         case di_enterschema: di_enterschema:
1277           if (di->keyname)
1278             di->dp = dataiterator_find_keyname(di, di->keyname);
1279           if (!di->dp || !*di->keyp)
1280             {
1281               if (di->kv.parent)
1282                 goto di_leavesub;
1283               goto di_nextrepodata;
1284             }
1285           /* FALLTHROUGH */
1286
1287         case di_enterkey: di_enterkey:
1288           di->kv.entry = -1;
1289           di->key = di->data->keys + *di->keyp;
1290           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
1291           if (!di->ddp)
1292             goto di_nextkey;
1293           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1294             goto di_enterarray;
1295           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1296             goto di_nextkey;
1297           /* FALLTHROUGH */
1298
1299         case di_nextattr:
1300           di->kv.entry++;
1301           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1302           if (di->kv.eof)
1303             di->state = di_nextkey;
1304           else
1305             di->state = di_nextattr;
1306           break;
1307
1308         case di_nextkey: di_nextkey:
1309           if (!di->keyname && *++di->keyp)
1310             goto di_enterkey;
1311           if (di->kv.parent)
1312             goto di_leavesub;
1313           /* FALLTHROUGH */
1314
1315         case di_nextrepodata: di_nextrepodata:
1316           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1317               goto di_enterrepodata;
1318           /* FALLTHROUGH */
1319
1320         case di_nextsolvable: di_nextsolvable:
1321           if (!(di->flags & SEARCH_THISSOLVID))
1322             {
1323               if (di->solvid < 0)
1324                 di->solvid = di->repo->start;
1325               else
1326                 di->solvid++;
1327               for (; di->solvid < di->repo->end; di->solvid++)
1328                 {
1329                   if (di->pool->solvables[di->solvid].repo == di->repo)
1330                     goto di_entersolvable;
1331                 }
1332             }
1333           /* FALLTHROUGH */
1334
1335         case di_nextrepo: di_nextrepo:
1336           if (di->repoid >= 0)
1337             {
1338               di->repoid++;
1339               di->repodataid = 0;
1340               if (di->repoid < di->pool->nrepos)
1341                 {
1342                   di->repo = di->pool->repos[di->repoid];
1343                   goto di_enterrepo;
1344                 }
1345             }
1346         /* FALLTHROUGH */
1347
1348         case di_bye: di_bye:
1349           di->state = di_bye;
1350           return 0;
1351
1352         case di_enterarray: di_enterarray:
1353           if (di->key->name == REPOSITORY_SOLVABLES)
1354             goto di_nextkey;
1355           di->ddp = data_read_id(di->ddp, &di->kv.num);
1356           di->kv.eof = 0;
1357           di->kv.entry = -1;
1358           /* FALLTHROUGH */
1359
1360         case di_nextarrayelement: di_nextarrayelement:
1361           di->kv.entry++;
1362           if (di->kv.entry)
1363             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1364           if (di->kv.entry == di->kv.num)
1365             {
1366               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1367                 goto di_nextkey;
1368               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1369                 goto di_nextkey;
1370               di->kv.str = (char *)di->ddp;
1371               di->kv.eof = 2;
1372               di->state = di_nextkey;
1373               break;
1374             }
1375           if (di->kv.entry == di->kv.num - 1)
1376             di->kv.eof = 1;
1377           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1378             di->ddp = data_read_id(di->ddp, &di->kv.id);
1379           di->kv.str = (char *)di->ddp;
1380           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1381             goto di_entersub;
1382           if ((di->flags & SEARCH_SUB) != 0)
1383             di->state = di_entersub;
1384           else
1385             di->state = di_nextarrayelement;
1386           break;
1387
1388         case di_entersub: di_entersub:
1389           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1390             goto di_nextarrayelement;   /* sorry, full */
1391           di->parents[di->nparents].kv = di->kv;
1392           di->parents[di->nparents].dp = di->dp;
1393           di->parents[di->nparents].keyp = di->keyp;
1394           di->dp = (unsigned char *)di->kv.str;
1395           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1396           memset(&di->kv, 0, sizeof(di->kv));
1397           di->kv.parent = &di->parents[di->nparents].kv;
1398           di->nparents++;
1399           di->keyname = di->keynames[di->nparents - di->rootlevel];
1400           goto di_enterschema;
1401
1402         case di_leavesub: di_leavesub:
1403           if (di->nparents - 1 < di->rootlevel)
1404             goto di_bye;
1405           di->nparents--;
1406           di->dp = di->parents[di->nparents].dp;
1407           di->kv = di->parents[di->nparents].kv;
1408           di->keyp = di->parents[di->nparents].keyp;
1409           di->key = di->data->keys + *di->keyp;
1410           di->ddp = (unsigned char *)di->kv.str;
1411           di->keyname = di->keynames[di->nparents - di->rootlevel];
1412           goto di_nextarrayelement;
1413
1414         /* special solvable attr handling follows */
1415
1416         case di_nextsolvableattr:
1417           di->kv.id = *di->idp++;
1418           di->kv.entry++;
1419           if (!*di->idp)
1420             {
1421               di->kv.eof = 1;
1422               di->state = di_nextsolvablekey;
1423             }
1424           break;
1425
1426         case di_nextsolvablekey: di_nextsolvablekey:
1427           if (di->keyname || di->key->name == RPM_RPMDBID)
1428             goto di_enterrepodata;
1429           di->key++;
1430           /* FALLTHROUGH */
1431
1432         case di_entersolvablekey: di_entersolvablekey:
1433           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1434           if (!di->idp || !di->idp[0])
1435             goto di_nextsolvablekey;
1436           di->kv.id = di->idp[0];
1437           di->kv.num = di->idp[0];
1438           di->idp++;
1439           if (!di->kv.eof && !di->idp[0])
1440             di->kv.eof = 1;
1441           di->kv.entry = 0;
1442           if (di->kv.eof)
1443             di->state = di_nextsolvablekey;
1444           else
1445             di->state = di_nextsolvableattr;
1446           break;
1447         }
1448
1449       if (di->matcher.match)
1450         {
1451           /* simple pre-check so that we don't need to stringify */
1452           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && di->matcher.match && (di->matcher.flags & (SEARCH_FILES|SEARCH_NOCASE|SEARCH_STRINGMASK)) == (SEARCH_FILES|SEARCH_STRING))
1453             {
1454               int l = strlen(di->matcher.match) - strlen(di->kv.str);
1455               if (l < 0 || strcmp(di->matcher.match + l, di->kv.str))
1456                 continue;
1457             }
1458           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1459             {
1460               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1461                 return 1;
1462               continue;
1463             }
1464           if (!datamatcher_match(&di->matcher, di->kv.str))
1465             continue;
1466         }
1467       /* found something! */
1468       return 1;
1469     }
1470 }
1471
1472 void
1473 dataiterator_entersub(Dataiterator *di)
1474 {
1475   if (di->state == di_nextarrayelement)
1476     di->state = di_entersub;
1477 }
1478
1479 void
1480 dataiterator_setpos(Dataiterator *di)
1481 {
1482   if (di->kv.eof == 2)
1483     {
1484       pool_clear_pos(di->pool);
1485       return;
1486     }
1487   di->pool->pos.solvid = di->solvid;
1488   di->pool->pos.repo = di->repo;
1489   di->pool->pos.repodataid = di->data - di->repo->repodata;
1490   di->pool->pos.schema = di->kv.id;
1491   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1492 }
1493
1494 void
1495 dataiterator_setpos_parent(Dataiterator *di)
1496 {
1497   if (!di->kv.parent || di->kv.parent->eof == 2)
1498     {
1499       pool_clear_pos(di->pool);
1500       return;
1501     }
1502   di->pool->pos.solvid = di->solvid;
1503   di->pool->pos.repo = di->repo;
1504   di->pool->pos.repodataid = di->data - di->repo->repodata;
1505   di->pool->pos.schema = di->kv.parent->id;
1506   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1507 }
1508
1509 /* clones just the position, not the search keys/matcher */
1510 void
1511 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1512 {
1513   di->state = from->state;
1514   di->flags &= ~SEARCH_THISSOLVID;
1515   di->flags |= (from->flags & SEARCH_THISSOLVID);
1516   di->repo = from->repo;
1517   di->data = from->data;
1518   di->dp = from->dp;
1519   di->ddp = from->ddp;
1520   di->idp = from->idp;
1521   di->keyp = from->keyp;
1522   di->key = from->key;
1523   di->kv = from->kv;
1524   di->repodataid = from->repodataid;
1525   di->solvid = from->solvid;
1526   di->repoid = from->repoid;
1527   di->rootlevel = from->rootlevel;
1528   memcpy(di->parents, from->parents, sizeof(from->parents));
1529   di->nparents = from->nparents;
1530   if (di->nparents)
1531     {
1532       int i;
1533       for (i = 1; i < di->nparents; i++)
1534         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1535       di->kv.parent = &di->parents[di->nparents - 1].kv;
1536     }
1537 }
1538
1539 void
1540 dataiterator_seek(Dataiterator *di, int whence)
1541 {
1542   if ((whence & DI_SEEK_STAY) != 0)
1543     di->rootlevel = di->nparents;
1544   switch (whence & ~DI_SEEK_STAY)
1545     {
1546     case DI_SEEK_CHILD:
1547       if (di->state != di_nextarrayelement)
1548         break;
1549       if ((whence & DI_SEEK_STAY) != 0)
1550         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1551       di->state = di_entersub;
1552       break;
1553     case DI_SEEK_PARENT:
1554       if (!di->nparents)
1555         {
1556           di->state = di_bye;
1557           break;
1558         }
1559       di->nparents--;
1560       if (di->rootlevel > di->nparents)
1561         di->rootlevel = di->nparents;
1562       di->dp = di->parents[di->nparents].dp;
1563       di->kv = di->parents[di->nparents].kv;
1564       di->keyp = di->parents[di->nparents].keyp;
1565       di->key = di->data->keys + *di->keyp;
1566       di->ddp = (unsigned char *)di->kv.str;
1567       di->keyname = di->keynames[di->nparents - di->rootlevel];
1568       di->state = di_nextarrayelement;
1569       break;
1570     case DI_SEEK_REWIND:
1571       if (!di->nparents)
1572         {
1573           di->state = di_bye;
1574           break;
1575         }
1576       di->dp = (unsigned char *)di->kv.parent->str;
1577       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1578       di->state = di_enterschema;
1579       break;
1580     default:
1581       break;
1582     }
1583 }
1584
1585 void
1586 dataiterator_skip_attribute(Dataiterator *di)
1587 {
1588   if (di->state == di_nextsolvableattr)
1589     di->state = di_nextsolvablekey;
1590   else
1591     di->state = di_nextkey;
1592 }
1593
1594 void
1595 dataiterator_skip_solvable(Dataiterator *di)
1596 {
1597   di->nparents = 0;
1598   di->rootlevel = 0;
1599   di->keyname = di->keynames[0];
1600   di->state = di_nextsolvable;
1601 }
1602
1603 void
1604 dataiterator_skip_repo(Dataiterator *di)
1605 {
1606   di->nparents = 0;
1607   di->rootlevel = 0;
1608   di->keyname = di->keynames[0];
1609   di->state = di_nextrepo;
1610 }
1611
1612 void
1613 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1614 {
1615   di->nparents = 0;
1616   di->rootlevel = 0;
1617   di->keyname = di->keynames[0];
1618   if (solvid == SOLVID_POS)
1619     {
1620       di->repo = di->pool->pos.repo;
1621       if (!di->repo)
1622         {
1623           di->state = di_bye;
1624           return;
1625         }
1626       di->repoid = -1;
1627       di->data = di->repo->repodata + di->pool->pos.repodataid;
1628       di->repodataid = -1;
1629       di->solvid = solvid;
1630       di->state = di_enterrepo;
1631       di->flags |= SEARCH_THISSOLVID;
1632       return;
1633     }
1634   if (solvid > 0)
1635     {
1636       di->repo = di->pool->solvables[solvid].repo;
1637       di->repoid = -1;
1638     }
1639   else if (di->repoid >= 0)
1640     {
1641       if (!di->pool->nrepos)
1642         {
1643           di->state = di_bye;
1644           return;
1645         }
1646       di->repo = di->pool->repos[0];
1647       di->repoid = 0;
1648     }
1649   di->repodataid = 0;
1650   di->solvid = solvid;
1651   if (solvid)
1652     di->flags |= SEARCH_THISSOLVID;
1653   di->state = di_enterrepo;
1654 }
1655
1656 void
1657 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1658 {
1659   di->nparents = 0;
1660   di->rootlevel = 0;
1661   di->repo = repo;
1662   di->repoid = -1;
1663   di->repodataid = 0;
1664   di->solvid = 0;
1665   di->flags &= ~SEARCH_THISSOLVID;
1666   di->state = di_enterrepo;
1667 }
1668
1669 int
1670 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1671 {
1672   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1673     return 0;
1674   if (!ma)
1675     return 1;
1676   return datamatcher_match(ma, di->kv.str);
1677 }
1678
1679 /************************************************************************
1680  * data modify functions
1681  */
1682
1683 /* extend repodata so that it includes solvables p */
1684 void
1685 repodata_extend(Repodata *data, Id p)
1686 {
1687   if (data->start == data->end)
1688     data->start = data->end = p;
1689   if (p >= data->end)
1690     {
1691       int old = data->end - data->start;
1692       int new = p - data->end + 1;
1693       if (data->attrs)
1694         {
1695           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1696           memset(data->attrs + old, 0, new * sizeof(Id *));
1697         }
1698       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1699       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1700       data->end = p + 1;
1701     }
1702   if (p < data->start)
1703     {
1704       int old = data->end - data->start;
1705       int new = data->start - p;
1706       if (data->attrs)
1707         {
1708           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1709           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1710           memset(data->attrs, 0, new * sizeof(Id *));
1711         }
1712       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1713       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1714       memset(data->incoreoffset, 0, new * sizeof(Id));
1715       data->start = p;
1716     }
1717 }
1718
1719 /* shrink end of repodata */
1720 void
1721 repodata_shrink(Repodata *data, int end)
1722 {
1723   int i;
1724
1725   if (data->end <= end)
1726     return;
1727   if (data->start >= end)
1728     {
1729       if (data->attrs)
1730         {
1731           for (i = 0; i < data->end - data->start; i++)
1732             sat_free(data->attrs[i]);
1733           data->attrs = sat_free(data->attrs);
1734         }
1735       data->incoreoffset = sat_free(data->incoreoffset);
1736       data->start = data->end = 0;
1737       return;
1738     }
1739   if (data->attrs)
1740     {
1741       for (i = end; i < data->end; i++)
1742         sat_free(data->attrs[i - data->start]);
1743       data->attrs = sat_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
1744     }
1745   if (data->incoreoffset)
1746     data->incoreoffset = sat_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
1747   data->end = end;
1748 }
1749
1750 /* extend repodata so that it includes solvables from start to start + num - 1 */
1751 void
1752 repodata_extend_block(Repodata *data, Id start, Id num)
1753 {
1754   if (!num)
1755     return;
1756   if (!data->incoreoffset)
1757     {
1758       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1759       data->start = start;
1760       data->end = start + num;
1761       return;
1762     }
1763   repodata_extend(data, start);
1764   if (num > 1)
1765     repodata_extend(data, start + num - 1);
1766 }
1767
1768 /**********************************************************************/
1769
1770
1771 #define REPODATA_ATTRS_BLOCK 31
1772 #define REPODATA_ATTRDATA_BLOCK 1023
1773 #define REPODATA_ATTRIDDATA_BLOCK 63
1774
1775
1776 Id
1777 repodata_new_handle(Repodata *data)
1778 {
1779   if (!data->nxattrs)
1780     {
1781       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1782       data->nxattrs = 2;
1783     }
1784   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1785   data->xattrs[data->nxattrs] = 0;
1786   return -(data->nxattrs++);
1787 }
1788
1789 static inline Id **
1790 repodata_get_attrp(Repodata *data, Id handle)
1791 {
1792   if (handle == SOLVID_META)
1793     {
1794       if (!data->xattrs)
1795         {
1796           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1797           data->nxattrs = 2;
1798         }
1799     }
1800   if (handle < 0)
1801     return data->xattrs - handle;
1802   if (handle < data->start || handle >= data->end)
1803     repodata_extend(data, handle);
1804   if (!data->attrs)
1805     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1806   return data->attrs + (handle - data->start);
1807 }
1808
1809 static void
1810 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1811 {
1812   Id *pp;
1813   Id *ap, **app;
1814   int i;
1815
1816   app = repodata_get_attrp(data, handle);
1817   ap = *app;
1818   i = 0;
1819   if (ap)
1820     {
1821       /* Determine equality based on the name only, allows us to change
1822          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1823       for (pp = ap; *pp; pp += 2)
1824         if (data->keys[*pp].name == data->keys[keyid].name)
1825           break;
1826       if (*pp)
1827         {
1828           if (overwrite)
1829             {
1830               pp[0] = keyid;
1831               pp[1] = val;
1832             }
1833           return;
1834         }
1835       i = pp - ap;
1836     }
1837   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1838   *app = ap;
1839   pp = ap + i;
1840   *pp++ = keyid;
1841   *pp++ = val;
1842   *pp = 0;
1843 }
1844
1845
1846 static void
1847 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1848 {
1849   Id keyid;
1850
1851   keyid = repodata_key2id(data, key, 1);
1852   repodata_insert_keyid(data, solvid, keyid, val, 1);
1853 }
1854
1855 void
1856 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1857 {
1858   Repokey key;
1859   key.name = keyname;
1860   key.type = REPOKEY_TYPE_ID;
1861   key.size = 0;
1862   key.storage = KEY_STORAGE_INCORE;
1863   repodata_set(data, solvid, &key, id);
1864 }
1865
1866 void
1867 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1868 {
1869   Repokey key;
1870   key.name = keyname;
1871   key.type = REPOKEY_TYPE_NUM;
1872   key.size = 0;
1873   key.storage = KEY_STORAGE_INCORE;
1874   repodata_set(data, solvid, &key, (Id)num);
1875 }
1876
1877 void
1878 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
1879 {
1880   Repokey key;
1881   Id id;
1882   if (data->localpool)
1883     id = stringpool_str2id(&data->spool, str, 1);
1884   else
1885     id = str2id(data->repo->pool, str, 1);
1886   key.name = keyname;
1887   key.type = REPOKEY_TYPE_ID;
1888   key.size = 0;
1889   key.storage = KEY_STORAGE_INCORE;
1890   repodata_set(data, solvid, &key, id);
1891 }
1892
1893 void
1894 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
1895 {
1896   Repokey key;
1897   key.name = keyname;
1898   key.type = REPOKEY_TYPE_CONSTANT;
1899   key.size = constant;
1900   key.storage = KEY_STORAGE_INCORE;
1901   repodata_set(data, solvid, &key, 0);
1902 }
1903
1904 void
1905 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
1906 {
1907   Repokey key;
1908   key.name = keyname;
1909   key.type = REPOKEY_TYPE_CONSTANTID;
1910   key.size = id;
1911   key.storage = KEY_STORAGE_INCORE;
1912   repodata_set(data, solvid, &key, 0);
1913 }
1914
1915 void
1916 repodata_set_void(Repodata *data, Id solvid, Id keyname)
1917 {
1918   Repokey key;
1919   key.name = keyname;
1920   key.type = REPOKEY_TYPE_VOID;
1921   key.size = 0;
1922   key.storage = KEY_STORAGE_INCORE;
1923   repodata_set(data, solvid, &key, 0);
1924 }
1925
1926 void
1927 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
1928 {
1929   Repokey key;
1930   int l;
1931
1932   l = strlen(str) + 1;
1933   key.name = keyname;
1934   key.type = REPOKEY_TYPE_STR;
1935   key.size = 0;
1936   key.storage = KEY_STORAGE_INCORE;
1937   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1938   memcpy(data->attrdata + data->attrdatalen, str, l);
1939   repodata_set(data, solvid, &key, data->attrdatalen);
1940   data->attrdatalen += l;
1941 }
1942
1943 void
1944 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
1945 {
1946   Repokey key;
1947   unsigned char *dp;
1948
1949   key.name = keyname;
1950   key.type = REPOKEY_TYPE_BINARY;
1951   key.size = 0;
1952   key.storage = KEY_STORAGE_INCORE;
1953   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
1954   dp = data->attrdata + data->attrdatalen;
1955   if (len >= (1 << 14))
1956     {
1957       if (len >= (1 << 28))
1958         *dp++ = (len >> 28) | 128;
1959       if (len >= (1 << 21))
1960         *dp++ = (len >> 21) | 128;
1961       *dp++ = (len >> 14) | 128;
1962     }
1963   if (len >= (1 << 7))
1964     *dp++ = (len >> 7) | 128;
1965   *dp++ = len & 127;
1966   if (len)
1967     memcpy(dp, buf, len);
1968   repodata_set(data, solvid, &key, data->attrdatalen);
1969   data->attrdatalen = dp + len - data->attrdata;
1970 }
1971
1972 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
1973  * so that the caller can append the new element there */
1974 static void
1975 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1976 {
1977   int oldsize;
1978   Id *ida, *pp, **ppp;
1979
1980   /* check if it is the same as last time, this speeds things up a lot */
1981   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1982     {
1983       /* great! just append the new data */
1984       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1985       data->attriddatalen--;    /* overwrite terminating 0  */
1986       data->lastdatalen += entrysize;
1987       return;
1988     }
1989
1990   ppp = repodata_get_attrp(data, handle);
1991   pp = *ppp;
1992   if (pp)
1993     for (; *pp; pp += 2)
1994       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1995         break;
1996   if (!pp || !*pp)
1997     {
1998       /* not found. allocate new key */
1999       Repokey key;
2000       key.name = keyname;
2001       key.type = keytype;
2002       key.size = 0;
2003       key.storage = KEY_STORAGE_INCORE;
2004       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2005       repodata_set(data, handle, &key, data->attriddatalen);
2006       data->lasthandle = 0;     /* next time... */
2007       return;
2008     }
2009   oldsize = 0;
2010   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2011     oldsize += entrysize;
2012   if (ida + 1 == data->attriddata + data->attriddatalen)
2013     {
2014       /* this was the last entry, just append it */
2015       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2016       data->attriddatalen--;    /* overwrite terminating 0  */
2017     }
2018   else
2019     {
2020       /* too bad. move to back. */
2021       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2022       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2023       pp[1] = data->attriddatalen;
2024       data->attriddatalen += oldsize;
2025     }
2026   data->lasthandle = handle;
2027   data->lastkey = *pp;
2028   data->lastdatalen = data->attriddatalen + entrysize + 1;
2029 }
2030
2031 void
2032 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2033                       const unsigned char *str)
2034 {
2035   Repokey key;
2036   int l;
2037
2038   if (!(l = sat_chksum_len(type)))
2039     return;
2040   key.name = keyname;
2041   key.type = type;
2042   key.size = 0;
2043   key.storage = KEY_STORAGE_INCORE;
2044   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2045   memcpy(data->attrdata + data->attrdatalen, str, l);
2046   repodata_set(data, solvid, &key, data->attrdatalen);
2047   data->attrdatalen += l;
2048 }
2049
2050 static int
2051 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2052 {
2053   int i;
2054   for (i = 0; i < buflen; i++)
2055     {
2056 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
2057                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
2058                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
2059                 : -1)
2060       int v = c2h(*str);
2061       str++;
2062       if (v < 0)
2063         return 0;
2064       buf[i] = v;
2065       v = c2h(*str);
2066       str++;
2067       if (v < 0)
2068         return 0;
2069       buf[i] = (buf[i] << 4) | v;
2070 #undef c2h
2071     }
2072   return buflen;
2073 }
2074
2075 void
2076 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2077                       const char *str)
2078 {
2079   unsigned char buf[64];
2080   int l;
2081
2082   if (!(l = sat_chksum_len(type)))
2083     return;
2084   if (hexstr2bytes(buf, str, l) != l)
2085     return;
2086   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2087 }
2088
2089 const char *
2090 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2091 {
2092   int i, l;
2093   char *str, *s;
2094
2095   if (!(l = sat_chksum_len(type)))
2096     return "";
2097   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2098   for (i = 0; i < l; i++)
2099     {
2100       unsigned char v = buf[i];
2101       unsigned char w = v >> 4;
2102       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2103       w = v & 15;
2104       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2105     }
2106   *s = 0;
2107   return str;
2108 }
2109
2110 /* rpm filenames don't contain the epoch, so strip it */
2111 static inline const char *
2112 evrid2vrstr(Pool *pool, Id evrid)
2113 {
2114   const char *p, *evr = id2str(pool, evrid);
2115   if (!evr)
2116     return evr;
2117   for (p = evr; *p >= '0' && *p <= '9'; p++)
2118     ;
2119   return p != evr && *p == ':' ? p + 1 : evr;
2120 }
2121
2122 void
2123 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2124 {
2125   Pool *pool = data->repo->pool;
2126   Solvable *s;
2127   const char *str, *fp;
2128   int l = 0;
2129
2130   if (medianr)
2131     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2132   if (!dir)
2133     {
2134       if ((dir = strrchr(file, '/')) != 0)
2135         {
2136           l = dir - file;
2137           dir = file;
2138           file = dir + l + 1;
2139           if (!l)
2140             l++;
2141         }
2142     }
2143   else
2144     l = strlen(dir);
2145   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2146     {
2147       dir += 2;
2148       l -= 2;
2149     }
2150   if (l == 1 && dir[0] == '.')
2151     l = 0;
2152   s = pool->solvables + solvid;
2153   if (dir && l)
2154     {
2155       str = id2str(pool, s->arch);
2156       if (!strncmp(dir, str, l) && !str[l])
2157         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2158       else if (!dir[l])
2159         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
2160       else
2161         {
2162           char *dir2 = strdup(dir);
2163           dir2[l] = 0;
2164           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
2165           free(dir2);
2166         }
2167     }
2168   fp = file;
2169   str = id2str(pool, s->name);
2170   l = strlen(str);
2171   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2172     {
2173       fp += l + 1;
2174       str = evrid2vrstr(pool, s->evr);
2175       l = strlen(str);
2176       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2177         {
2178           fp += l + 1;
2179           str = id2str(pool, s->arch);
2180           l = strlen(str);
2181           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2182             {
2183               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2184               return;
2185             }
2186         }
2187     }
2188   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2189 }
2190
2191 void
2192 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2193 {
2194   assert(dir);
2195 #if 0
2196 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2197 #endif
2198   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2199   data->attriddata[data->attriddatalen++] = dir;
2200   data->attriddata[data->attriddatalen++] = num;
2201   data->attriddata[data->attriddatalen++] = num2;
2202   data->attriddata[data->attriddatalen++] = 0;
2203 }
2204
2205 void
2206 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2207 {
2208   Id stroff;
2209   int l;
2210
2211   assert(dir);
2212   l = strlen(str) + 1;
2213   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2214   memcpy(data->attrdata + data->attrdatalen, str, l);
2215   stroff = data->attrdatalen;
2216   data->attrdatalen += l;
2217
2218 #if 0
2219 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2220 #endif
2221   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2222   data->attriddata[data->attriddatalen++] = dir;
2223   data->attriddata[data->attriddatalen++] = stroff;
2224   data->attriddata[data->attriddatalen++] = 0;
2225 }
2226
2227 void
2228 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2229 {
2230 #if 0
2231 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2232 #endif
2233   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2234   data->attriddata[data->attriddatalen++] = id;
2235   data->attriddata[data->attriddatalen++] = 0;
2236 }
2237
2238 void
2239 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2240                            const char *str)
2241 {
2242   Id id;
2243   if (data->localpool)
2244     id = stringpool_str2id(&data->spool, str, 1);
2245   else
2246     id = str2id(data->repo->pool, str, 1);
2247   repodata_add_idarray(data, solvid, keyname, id);
2248 }
2249
2250 void
2251 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2252 {
2253   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2254   data->attriddata[data->attriddatalen++] = ghandle;
2255   data->attriddata[data->attriddatalen++] = 0;
2256 }
2257
2258 void
2259 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2260 {
2261   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2262   data->attriddata[data->attriddatalen++] = ghandle;
2263   data->attriddata[data->attriddatalen++] = 0;
2264 }
2265
2266 /* add all attrs from src to dest */
2267 void
2268 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2269 {
2270   Id *keyp;
2271   if (dest == src || !(keyp = data->attrs[src - data->start]))
2272     return;
2273   for (; *keyp; keyp += 2)
2274     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2275 }
2276
2277 void
2278 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2279 {
2280   Id *keyp;
2281   if (dest == src || !(keyp = data->attrs[src - data->start]))
2282     return;
2283   for (; *keyp; keyp += 2)
2284     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2285       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2286 }
2287
2288
2289
2290 /**********************************************************************/
2291
2292 /* TODO: unify with repo_write and repo_solv! */
2293
2294 #define EXTDATA_BLOCK 1023
2295
2296 struct extdata {
2297   unsigned char *buf;
2298   int len;
2299 };
2300
2301 static void
2302 data_addid(struct extdata *xd, Id x)
2303 {
2304   unsigned char *dp;
2305
2306   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2307   dp = xd->buf + xd->len;
2308
2309   if (x >= (1 << 14))
2310     {
2311       if (x >= (1 << 28))
2312         *dp++ = (x >> 28) | 128;
2313       if (x >= (1 << 21))
2314         *dp++ = (x >> 21) | 128;
2315       *dp++ = (x >> 14) | 128;
2316     }
2317   if (x >= (1 << 7))
2318     *dp++ = (x >> 7) | 128;
2319   *dp++ = x & 127;
2320   xd->len = dp - xd->buf;
2321 }
2322
2323 static void
2324 data_addideof(struct extdata *xd, Id x, int eof)
2325 {
2326   if (x >= 64)
2327     x = (x & 63) | ((x & ~63) << 1);
2328   data_addid(xd, (eof ? x : x | 64));
2329 }
2330
2331 static void
2332 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2333 {
2334   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2335   memcpy(xd->buf + xd->len, blob, len);
2336   xd->len += len;
2337 }
2338
2339 /*********************************/
2340
2341 static void
2342 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2343                        struct extdata *newvincore,
2344                        Id *schema,
2345                        Repokey *key, Id val)
2346 {
2347   /* Otherwise we have a new value.  Parse it into the internal
2348      form.  */
2349   Id *ida;
2350   struct extdata *xd;
2351   unsigned int oldvincorelen = 0;
2352   Id schemaid, *sp;
2353
2354   xd = newincore;
2355   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2356     {
2357       xd = newvincore;
2358       oldvincorelen = xd->len;
2359     }
2360   switch (key->type)
2361     {
2362     case REPOKEY_TYPE_VOID:
2363     case REPOKEY_TYPE_CONSTANT:
2364     case REPOKEY_TYPE_CONSTANTID:
2365       break;
2366     case REPOKEY_TYPE_STR:
2367       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2368       break;
2369     case REPOKEY_TYPE_MD5:
2370       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2371       break;
2372     case REPOKEY_TYPE_SHA1:
2373       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2374       break;
2375     case REPOKEY_TYPE_SHA256:
2376       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2377       break;
2378     case REPOKEY_TYPE_ID:
2379     case REPOKEY_TYPE_NUM:
2380     case REPOKEY_TYPE_DIR:
2381       data_addid(xd, val);
2382       break;
2383     case REPOKEY_TYPE_BINARY:
2384       {
2385         Id len;
2386         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2387         dp += len;
2388         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2389       }
2390       break;
2391     case REPOKEY_TYPE_IDARRAY:
2392       for (ida = data->attriddata + val; *ida; ida++)
2393         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2394       break;
2395     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2396       for (ida = data->attriddata + val; *ida; ida += 3)
2397         {
2398           data_addid(xd, ida[0]);
2399           data_addid(xd, ida[1]);
2400           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2401         }
2402       break;
2403     case REPOKEY_TYPE_DIRSTRARRAY:
2404       for (ida = data->attriddata + val; *ida; ida += 2)
2405         {
2406           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2407           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2408         }
2409       break;
2410     case REPOKEY_TYPE_FIXARRAY:
2411       {
2412         int num = 0;
2413         schemaid = 0;
2414         for (ida = data->attriddata + val; *ida; ida++)
2415           {
2416 #if 0
2417             fprintf(stderr, "serialize struct %d\n", *ida);
2418 #endif
2419             sp = schema;
2420             Id *kp = data->xattrs[-*ida];
2421             if (!kp)
2422               continue;
2423             num++;
2424             for (;*kp; kp += 2)
2425               {
2426 #if 0
2427                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2428 #endif
2429                 *sp++ = *kp;
2430               }
2431             *sp = 0;
2432             if (!schemaid)
2433               schemaid = repodata_schema2id(data, schema, 1);
2434             else if (schemaid != repodata_schema2id(data, schema, 0))
2435               {
2436                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
2437                 exit(1);
2438               }
2439 #if 0
2440             fprintf(stderr, "  schema %d\n", schemaid);
2441 #endif
2442           }
2443         if (!num)
2444           break;
2445         data_addid(xd, num);
2446         data_addid(xd, schemaid);
2447         for (ida = data->attriddata + val; *ida; ida++)
2448           {
2449             Id *kp = data->xattrs[-*ida];
2450             if (!kp)
2451               continue;
2452             for (;*kp; kp += 2)
2453               {
2454                 repodata_serialize_key(data, newincore, newvincore,
2455                                        schema, data->keys + *kp, kp[1]);
2456               }
2457           }
2458         break;
2459       }
2460     case REPOKEY_TYPE_FLEXARRAY:
2461       {
2462         int num = 0;
2463         for (ida = data->attriddata + val; *ida; ida++)
2464           num++;
2465         data_addid(xd, num);
2466         for (ida = data->attriddata + val; *ida; ida++)
2467           {
2468             Id *kp = data->xattrs[-*ida];
2469             if (!kp)
2470               {
2471                 data_addid(xd, 0);      /* XXX */
2472                 continue;
2473               }
2474             sp = schema;
2475             for (;*kp; kp += 2)
2476               *sp++ = *kp;
2477             *sp = 0;
2478             schemaid = repodata_schema2id(data, schema, 1);
2479             data_addid(xd, schemaid);
2480             kp = data->xattrs[-*ida];
2481             for (;*kp; kp += 2)
2482               {
2483                 repodata_serialize_key(data, newincore, newvincore,
2484                                        schema, data->keys + *kp, kp[1]);
2485               }
2486           }
2487         break;
2488       }
2489     default:
2490       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
2491       exit(1);
2492     }
2493   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2494     {
2495       /* put offset/len in incore */
2496       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2497       oldvincorelen = xd->len - oldvincorelen;
2498       data_addid(newincore, oldvincorelen);
2499     }
2500 }
2501
2502 void
2503 repodata_internalize(Repodata *data)
2504 {
2505   Repokey *key, solvkey;
2506   Id entry, nentry;
2507   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2508   unsigned char *dp, *ndp;
2509   int newschema, oldcount;
2510   struct extdata newincore;
2511   struct extdata newvincore;
2512   Id solvkeyid;
2513
2514   if (!data->attrs && !data->xattrs)
2515     return;
2516
2517   newvincore.buf = data->vincore;
2518   newvincore.len = data->vincorelen;
2519
2520   /* find the solvables key, create if needed */
2521   memset(&solvkey, 0, sizeof(solvkey));
2522   solvkey.name = REPOSITORY_SOLVABLES;
2523   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2524   solvkey.size = 0;
2525   solvkey.storage = KEY_STORAGE_INCORE;
2526   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2527
2528   schema = sat_malloc2(data->nkeys, sizeof(Id));
2529   seen = sat_malloc2(data->nkeys, sizeof(Id));
2530
2531   /* Merge the data already existing (in data->schemata, ->incoredata and
2532      friends) with the new attributes in data->attrs[].  */
2533   nentry = data->end - data->start;
2534   memset(&newincore, 0, sizeof(newincore));
2535   data_addid(&newincore, 0);    /* start data at offset 1 */
2536
2537   data->mainschema = 0;
2538   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2539
2540   /* join entry data */
2541   /* we start with the meta data, entry -1 */
2542   for (entry = -1; entry < nentry; entry++)
2543     {
2544       memset(seen, 0, data->nkeys * sizeof(Id));
2545       oldschema = 0;
2546       dp = data->incoredata;
2547       if (dp)
2548         {
2549           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2550           dp = data_read_id(dp, &oldschema);
2551         }
2552 #if 0
2553 fprintf(stderr, "oldschema %d\n", oldschema);
2554 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2555 fprintf(stderr, "schemadata %p\n", data->schemadata);
2556 #endif
2557       /* seen: -1: old data  0: skipped  >0: id + 1 */
2558       newschema = 0;
2559       oldcount = 0;
2560       sp = schema;
2561       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2562         {
2563           if (seen[*keyp])
2564             {
2565               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
2566               exit(1);
2567             }
2568           seen[*keyp] = -1;
2569           *sp++ = *keyp;
2570           oldcount++;
2571         }
2572       if (entry >= 0)
2573         keyp = data->attrs ? data->attrs[entry] : 0;
2574       else
2575         {
2576           /* strip solvables key */
2577           *sp = 0;
2578           for (sp = keyp = schema; *sp; sp++)
2579             if (*sp != solvkeyid)
2580               *keyp++ = *sp;
2581             else
2582               oldcount--;
2583           sp = keyp;
2584           seen[solvkeyid] = 0;
2585           keyp = data->xattrs ? data->xattrs[1] : 0;
2586         }
2587       if (keyp)
2588         for (; *keyp; keyp += 2)
2589           {
2590             if (!seen[*keyp])
2591               {
2592                 newschema = 1;
2593                 *sp++ = *keyp;
2594               }
2595             seen[*keyp] = keyp[1] + 1;
2596           }
2597       if (entry < 0 && data->end != data->start)
2598         {
2599           *sp++ = solvkeyid;
2600           newschema = 1;
2601         }
2602       *sp = 0;
2603       if (newschema)
2604         /* Ideally we'd like to sort the new schema here, to ensure
2605            schema equality independend of the ordering.  We can't do that
2606            yet.  For once see below (old ids need to come before new ids).
2607            An additional difficulty is that we also need to move
2608            the values with the keys.  */
2609         schemaid = repodata_schema2id(data, schema, 1);
2610       else
2611         schemaid = oldschema;
2612
2613
2614       /* Now create data blob.  We walk through the (possibly new) schema
2615          and either copy over old data, or insert the new.  */
2616       /* XXX Here we rely on the fact that the (new) schema has the form
2617          o1 o2 o3 o4 ... | n1 n2 n3 ...
2618          (oX being the old keyids (possibly overwritten), and nX being
2619           the new keyids).  This rules out sorting the keyids in order
2620          to ensure a small schema count.  */
2621       if (entry >= 0)
2622         data->incoreoffset[entry] = newincore.len;
2623       data_addid(&newincore, schemaid);
2624       if (entry == -1)
2625         {
2626           data->mainschema = schemaid;
2627           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2628         }
2629       keypstart = data->schemadata + data->schemata[schemaid];
2630       for (keyp = keypstart; *keyp; keyp++)
2631         {
2632           if (entry == -1)
2633             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2634           if (*keyp == solvkeyid)
2635             {
2636               /* add flexarray entry count */
2637               data_addid(&newincore, data->end - data->start);
2638               break;
2639             }
2640           key = data->keys + *keyp;
2641 #if 0
2642           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2643 #endif
2644           ndp = dp;
2645           if (oldcount)
2646             {
2647               /* Skip the data associated with this old key.  */
2648               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2649                 {
2650                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2651                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2652                 }
2653               else if (key->storage == KEY_STORAGE_INCORE)
2654                 ndp = data_skip_key(data, dp, key);
2655               oldcount--;
2656             }
2657           if (seen[*keyp] == -1)
2658             {
2659               /* If this key was an old one _and_ was not overwritten with
2660                  a different value copy over the old value (we skipped it
2661                  above).  */
2662               if (dp != ndp)
2663                 data_addblob(&newincore, dp, ndp - dp);
2664               seen[*keyp] = 0;
2665             }
2666           else if (seen[*keyp])
2667             {
2668               /* Otherwise we have a new value.  Parse it into the internal
2669                  form.  */
2670               repodata_serialize_key(data, &newincore, &newvincore,
2671                                      schema, key, seen[*keyp] - 1);
2672             }
2673           dp = ndp;
2674         }
2675       if (entry >= 0 && data->attrs && data->attrs[entry])
2676         data->attrs[entry] = sat_free(data->attrs[entry]);
2677     }
2678   /* free all xattrs */
2679   for (entry = 0; entry < data->nxattrs; entry++)
2680     if (data->xattrs[entry])
2681       sat_free(data->xattrs[entry]);
2682   data->xattrs = sat_free(data->xattrs);
2683   data->nxattrs = 0;
2684
2685   data->lasthandle = 0;
2686   data->lastkey = 0;
2687   data->lastdatalen = 0;
2688   sat_free(schema);
2689   sat_free(seen);
2690   repodata_free_schemahash(data);
2691
2692   sat_free(data->incoredata);
2693   data->incoredata = newincore.buf;
2694   data->incoredatalen = newincore.len;
2695   data->incoredatafree = 0;
2696
2697   sat_free(data->vincore);
2698   data->vincore = newvincore.buf;
2699   data->vincorelen = newvincore.len;
2700
2701   data->attrs = sat_free(data->attrs);
2702   data->attrdata = sat_free(data->attrdata);
2703   data->attriddata = sat_free(data->attriddata);
2704   data->attrdatalen = 0;
2705   data->attriddatalen = 0;
2706 }
2707
2708 void
2709 repodata_disable_paging(Repodata *data)
2710 {
2711   if (maybe_load_repodata(data, 0))
2712     repopagestore_disable_paging(&data->store);
2713 }
2714
2715 static void
2716 repodata_load_stub(Repodata *data)
2717 {
2718   Repo *repo = data->repo;
2719   Pool *pool = repo->pool;
2720   int r;
2721
2722   if (!pool->loadcallback)
2723     {
2724       data->state = REPODATA_ERROR;
2725       return;
2726     }
2727   data->state = REPODATA_LOADING;
2728   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
2729   if (!r)
2730     data->state = REPODATA_ERROR;
2731 }
2732
2733 void
2734 repodata_create_stubs(Repodata *data)
2735 {
2736   Repo *repo = data->repo;
2737   Pool *pool = repo->pool;
2738   Repodata *sdata;
2739   int *stubdataids;
2740   Dataiterator di;
2741   Id xkeyname = 0;
2742   int i, cnt = 0;
2743   int repodataid;
2744   int datastart, dataend;
2745
2746   repodataid = data - repo->repodata;
2747   datastart = data->start;
2748   dataend = data->end;
2749   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2750   while (dataiterator_step(&di))
2751     {
2752       if (di.data - repo->repodata != repodataid)
2753         continue;
2754       cnt++;
2755     }
2756   dataiterator_free(&di);
2757   if (!cnt)
2758     return;
2759   stubdataids = sat_calloc(cnt, sizeof(*stubdataids));
2760   for (i = 0; i < cnt; i++)
2761     {
2762       sdata = repo_add_repodata(repo, 0);
2763       if (dataend > datastart)
2764         repodata_extend_block(sdata, datastart, dataend - datastart);
2765       stubdataids[i] = sdata - repo->repodata;
2766       sdata->state = REPODATA_STUB;
2767       sdata->loadcallback = repodata_load_stub;
2768     }
2769   i = 0;
2770   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2771   sdata = 0;
2772   while (dataiterator_step(&di))
2773     {
2774       if (di.data - repo->repodata != repodataid)
2775         continue;
2776       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
2777         {
2778           dataiterator_entersub(&di);
2779           sdata = repo->repodata + stubdataids[i++];
2780           xkeyname = 0;
2781           continue;
2782         }
2783       switch (di.key->type)
2784         {
2785         case REPOKEY_TYPE_ID:
2786           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
2787           break;
2788         case REPOKEY_TYPE_CONSTANTID:
2789           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
2790           break;
2791         case REPOKEY_TYPE_STR:
2792           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
2793           break;
2794         case REPOKEY_TYPE_VOID:
2795           repodata_set_void(sdata, SOLVID_META, di.key->name);
2796           break;
2797         case REPOKEY_TYPE_NUM:
2798           repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
2799           break;
2800         case REPOKEY_TYPE_MD5:
2801         case REPOKEY_TYPE_SHA1:
2802         case REPOKEY_TYPE_SHA256:
2803           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
2804           break;
2805         case REPOKEY_TYPE_IDARRAY:
2806           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
2807           if (di.key->name == REPOSITORY_KEYS)
2808             {
2809               Repokey xkey;
2810
2811               if (!xkeyname)
2812                 {
2813                   if (!di.kv.eof)
2814                     xkeyname = di.kv.id;
2815                   continue;
2816                 }
2817               xkey.name = xkeyname;
2818               xkey.type = di.kv.id;
2819               xkey.storage = KEY_STORAGE_INCORE;
2820               xkey.size = 0; 
2821               repodata_key2id(sdata, &xkey, 1);
2822               xkeyname = 0;
2823             }
2824         }
2825     }
2826   dataiterator_free(&di);
2827   for (i = 0; i < cnt; i++)
2828     repodata_internalize(repo->repodata + stubdataids[i]);
2829   sat_free(stubdataids);
2830 }
2831
2832 /*
2833 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2834 */