- dataiterator: reset parent when jumping to a solvid [bnc#589640]
[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->kv.parent = 0;
1599   di->rootlevel = 0;
1600   di->keyname = di->keynames[0];
1601   di->state = di_nextsolvable;
1602 }
1603
1604 void
1605 dataiterator_skip_repo(Dataiterator *di)
1606 {
1607   di->nparents = 0;
1608   di->kv.parent = 0;
1609   di->rootlevel = 0;
1610   di->keyname = di->keynames[0];
1611   di->state = di_nextrepo;
1612 }
1613
1614 void
1615 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1616 {
1617   di->nparents = 0;
1618   di->kv.parent = 0;
1619   di->rootlevel = 0;
1620   di->keyname = di->keynames[0];
1621   if (solvid == SOLVID_POS)
1622     {
1623       di->repo = di->pool->pos.repo;
1624       if (!di->repo)
1625         {
1626           di->state = di_bye;
1627           return;
1628         }
1629       di->repoid = -1;
1630       di->data = di->repo->repodata + di->pool->pos.repodataid;
1631       di->repodataid = -1;
1632       di->solvid = solvid;
1633       di->state = di_enterrepo;
1634       di->flags |= SEARCH_THISSOLVID;
1635       return;
1636     }
1637   if (solvid > 0)
1638     {
1639       di->repo = di->pool->solvables[solvid].repo;
1640       di->repoid = -1;
1641     }
1642   else if (di->repoid >= 0)
1643     {
1644       if (!di->pool->nrepos)
1645         {
1646           di->state = di_bye;
1647           return;
1648         }
1649       di->repo = di->pool->repos[0];
1650       di->repoid = 0;
1651     }
1652   di->repodataid = 0;
1653   di->solvid = solvid;
1654   if (solvid)
1655     di->flags |= SEARCH_THISSOLVID;
1656   di->state = di_enterrepo;
1657 }
1658
1659 void
1660 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1661 {
1662   di->nparents = 0;
1663   di->kv.parent = 0;
1664   di->rootlevel = 0;
1665   di->repo = repo;
1666   di->repoid = -1;
1667   di->repodataid = 0;
1668   di->solvid = 0;
1669   di->flags &= ~SEARCH_THISSOLVID;
1670   di->state = di_enterrepo;
1671 }
1672
1673 int
1674 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1675 {
1676   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1677     return 0;
1678   if (!ma)
1679     return 1;
1680   return datamatcher_match(ma, di->kv.str);
1681 }
1682
1683 /************************************************************************
1684  * data modify functions
1685  */
1686
1687 /* extend repodata so that it includes solvables p */
1688 void
1689 repodata_extend(Repodata *data, Id p)
1690 {
1691   if (data->start == data->end)
1692     data->start = data->end = p;
1693   if (p >= data->end)
1694     {
1695       int old = data->end - data->start;
1696       int new = p - data->end + 1;
1697       if (data->attrs)
1698         {
1699           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1700           memset(data->attrs + old, 0, new * sizeof(Id *));
1701         }
1702       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1703       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1704       data->end = p + 1;
1705     }
1706   if (p < data->start)
1707     {
1708       int old = data->end - data->start;
1709       int new = data->start - p;
1710       if (data->attrs)
1711         {
1712           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1713           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1714           memset(data->attrs, 0, new * sizeof(Id *));
1715         }
1716       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1717       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1718       memset(data->incoreoffset, 0, new * sizeof(Id));
1719       data->start = p;
1720     }
1721 }
1722
1723 /* shrink end of repodata */
1724 void
1725 repodata_shrink(Repodata *data, int end)
1726 {
1727   int i;
1728
1729   if (data->end <= end)
1730     return;
1731   if (data->start >= end)
1732     {
1733       if (data->attrs)
1734         {
1735           for (i = 0; i < data->end - data->start; i++)
1736             sat_free(data->attrs[i]);
1737           data->attrs = sat_free(data->attrs);
1738         }
1739       data->incoreoffset = sat_free(data->incoreoffset);
1740       data->start = data->end = 0;
1741       return;
1742     }
1743   if (data->attrs)
1744     {
1745       for (i = end; i < data->end; i++)
1746         sat_free(data->attrs[i - data->start]);
1747       data->attrs = sat_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
1748     }
1749   if (data->incoreoffset)
1750     data->incoreoffset = sat_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
1751   data->end = end;
1752 }
1753
1754 /* extend repodata so that it includes solvables from start to start + num - 1 */
1755 void
1756 repodata_extend_block(Repodata *data, Id start, Id num)
1757 {
1758   if (!num)
1759     return;
1760   if (!data->incoreoffset)
1761     {
1762       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1763       data->start = start;
1764       data->end = start + num;
1765       return;
1766     }
1767   repodata_extend(data, start);
1768   if (num > 1)
1769     repodata_extend(data, start + num - 1);
1770 }
1771
1772 /**********************************************************************/
1773
1774
1775 #define REPODATA_ATTRS_BLOCK 31
1776 #define REPODATA_ATTRDATA_BLOCK 1023
1777 #define REPODATA_ATTRIDDATA_BLOCK 63
1778
1779
1780 Id
1781 repodata_new_handle(Repodata *data)
1782 {
1783   if (!data->nxattrs)
1784     {
1785       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1786       data->nxattrs = 2;
1787     }
1788   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1789   data->xattrs[data->nxattrs] = 0;
1790   return -(data->nxattrs++);
1791 }
1792
1793 static inline Id **
1794 repodata_get_attrp(Repodata *data, Id handle)
1795 {
1796   if (handle == SOLVID_META)
1797     {
1798       if (!data->xattrs)
1799         {
1800           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1801           data->nxattrs = 2;
1802         }
1803     }
1804   if (handle < 0)
1805     return data->xattrs - handle;
1806   if (handle < data->start || handle >= data->end)
1807     repodata_extend(data, handle);
1808   if (!data->attrs)
1809     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1810   return data->attrs + (handle - data->start);
1811 }
1812
1813 static void
1814 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1815 {
1816   Id *pp;
1817   Id *ap, **app;
1818   int i;
1819
1820   app = repodata_get_attrp(data, handle);
1821   ap = *app;
1822   i = 0;
1823   if (ap)
1824     {
1825       /* Determine equality based on the name only, allows us to change
1826          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1827       for (pp = ap; *pp; pp += 2)
1828         if (data->keys[*pp].name == data->keys[keyid].name)
1829           break;
1830       if (*pp)
1831         {
1832           if (overwrite)
1833             {
1834               pp[0] = keyid;
1835               pp[1] = val;
1836             }
1837           return;
1838         }
1839       i = pp - ap;
1840     }
1841   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1842   *app = ap;
1843   pp = ap + i;
1844   *pp++ = keyid;
1845   *pp++ = val;
1846   *pp = 0;
1847 }
1848
1849
1850 static void
1851 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1852 {
1853   Id keyid;
1854
1855   keyid = repodata_key2id(data, key, 1);
1856   repodata_insert_keyid(data, solvid, keyid, val, 1);
1857 }
1858
1859 void
1860 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1861 {
1862   Repokey key;
1863   key.name = keyname;
1864   key.type = REPOKEY_TYPE_ID;
1865   key.size = 0;
1866   key.storage = KEY_STORAGE_INCORE;
1867   repodata_set(data, solvid, &key, id);
1868 }
1869
1870 void
1871 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1872 {
1873   Repokey key;
1874   key.name = keyname;
1875   key.type = REPOKEY_TYPE_NUM;
1876   key.size = 0;
1877   key.storage = KEY_STORAGE_INCORE;
1878   repodata_set(data, solvid, &key, (Id)num);
1879 }
1880
1881 void
1882 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
1883 {
1884   Repokey key;
1885   Id id;
1886   if (data->localpool)
1887     id = stringpool_str2id(&data->spool, str, 1);
1888   else
1889     id = str2id(data->repo->pool, str, 1);
1890   key.name = keyname;
1891   key.type = REPOKEY_TYPE_ID;
1892   key.size = 0;
1893   key.storage = KEY_STORAGE_INCORE;
1894   repodata_set(data, solvid, &key, id);
1895 }
1896
1897 void
1898 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
1899 {
1900   Repokey key;
1901   key.name = keyname;
1902   key.type = REPOKEY_TYPE_CONSTANT;
1903   key.size = constant;
1904   key.storage = KEY_STORAGE_INCORE;
1905   repodata_set(data, solvid, &key, 0);
1906 }
1907
1908 void
1909 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
1910 {
1911   Repokey key;
1912   key.name = keyname;
1913   key.type = REPOKEY_TYPE_CONSTANTID;
1914   key.size = id;
1915   key.storage = KEY_STORAGE_INCORE;
1916   repodata_set(data, solvid, &key, 0);
1917 }
1918
1919 void
1920 repodata_set_void(Repodata *data, Id solvid, Id keyname)
1921 {
1922   Repokey key;
1923   key.name = keyname;
1924   key.type = REPOKEY_TYPE_VOID;
1925   key.size = 0;
1926   key.storage = KEY_STORAGE_INCORE;
1927   repodata_set(data, solvid, &key, 0);
1928 }
1929
1930 void
1931 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
1932 {
1933   Repokey key;
1934   int l;
1935
1936   l = strlen(str) + 1;
1937   key.name = keyname;
1938   key.type = REPOKEY_TYPE_STR;
1939   key.size = 0;
1940   key.storage = KEY_STORAGE_INCORE;
1941   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1942   memcpy(data->attrdata + data->attrdatalen, str, l);
1943   repodata_set(data, solvid, &key, data->attrdatalen);
1944   data->attrdatalen += l;
1945 }
1946
1947 void
1948 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
1949 {
1950   Repokey key;
1951   unsigned char *dp;
1952
1953   key.name = keyname;
1954   key.type = REPOKEY_TYPE_BINARY;
1955   key.size = 0;
1956   key.storage = KEY_STORAGE_INCORE;
1957   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
1958   dp = data->attrdata + data->attrdatalen;
1959   if (len >= (1 << 14))
1960     {
1961       if (len >= (1 << 28))
1962         *dp++ = (len >> 28) | 128;
1963       if (len >= (1 << 21))
1964         *dp++ = (len >> 21) | 128;
1965       *dp++ = (len >> 14) | 128;
1966     }
1967   if (len >= (1 << 7))
1968     *dp++ = (len >> 7) | 128;
1969   *dp++ = len & 127;
1970   if (len)
1971     memcpy(dp, buf, len);
1972   repodata_set(data, solvid, &key, data->attrdatalen);
1973   data->attrdatalen = dp + len - data->attrdata;
1974 }
1975
1976 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
1977  * so that the caller can append the new element there */
1978 static void
1979 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1980 {
1981   int oldsize;
1982   Id *ida, *pp, **ppp;
1983
1984   /* check if it is the same as last time, this speeds things up a lot */
1985   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1986     {
1987       /* great! just append the new data */
1988       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1989       data->attriddatalen--;    /* overwrite terminating 0  */
1990       data->lastdatalen += entrysize;
1991       return;
1992     }
1993
1994   ppp = repodata_get_attrp(data, handle);
1995   pp = *ppp;
1996   if (pp)
1997     for (; *pp; pp += 2)
1998       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1999         break;
2000   if (!pp || !*pp)
2001     {
2002       /* not found. allocate new key */
2003       Repokey key;
2004       key.name = keyname;
2005       key.type = keytype;
2006       key.size = 0;
2007       key.storage = KEY_STORAGE_INCORE;
2008       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2009       repodata_set(data, handle, &key, data->attriddatalen);
2010       data->lasthandle = 0;     /* next time... */
2011       return;
2012     }
2013   oldsize = 0;
2014   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2015     oldsize += entrysize;
2016   if (ida + 1 == data->attriddata + data->attriddatalen)
2017     {
2018       /* this was the last entry, just append it */
2019       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2020       data->attriddatalen--;    /* overwrite terminating 0  */
2021     }
2022   else
2023     {
2024       /* too bad. move to back. */
2025       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2026       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2027       pp[1] = data->attriddatalen;
2028       data->attriddatalen += oldsize;
2029     }
2030   data->lasthandle = handle;
2031   data->lastkey = *pp;
2032   data->lastdatalen = data->attriddatalen + entrysize + 1;
2033 }
2034
2035 void
2036 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2037                       const unsigned char *str)
2038 {
2039   Repokey key;
2040   int l;
2041
2042   if (!(l = sat_chksum_len(type)))
2043     return;
2044   key.name = keyname;
2045   key.type = type;
2046   key.size = 0;
2047   key.storage = KEY_STORAGE_INCORE;
2048   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2049   memcpy(data->attrdata + data->attrdatalen, str, l);
2050   repodata_set(data, solvid, &key, data->attrdatalen);
2051   data->attrdatalen += l;
2052 }
2053
2054 static int
2055 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2056 {
2057   int i;
2058   for (i = 0; i < buflen; i++)
2059     {
2060 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
2061                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
2062                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
2063                 : -1)
2064       int v = c2h(*str);
2065       str++;
2066       if (v < 0)
2067         return 0;
2068       buf[i] = v;
2069       v = c2h(*str);
2070       str++;
2071       if (v < 0)
2072         return 0;
2073       buf[i] = (buf[i] << 4) | v;
2074 #undef c2h
2075     }
2076   return buflen;
2077 }
2078
2079 void
2080 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2081                       const char *str)
2082 {
2083   unsigned char buf[64];
2084   int l;
2085
2086   if (!(l = sat_chksum_len(type)))
2087     return;
2088   if (hexstr2bytes(buf, str, l) != l)
2089     return;
2090   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2091 }
2092
2093 const char *
2094 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2095 {
2096   int i, l;
2097   char *str, *s;
2098
2099   if (!(l = sat_chksum_len(type)))
2100     return "";
2101   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2102   for (i = 0; i < l; i++)
2103     {
2104       unsigned char v = buf[i];
2105       unsigned char w = v >> 4;
2106       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2107       w = v & 15;
2108       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2109     }
2110   *s = 0;
2111   return str;
2112 }
2113
2114 /* rpm filenames don't contain the epoch, so strip it */
2115 static inline const char *
2116 evrid2vrstr(Pool *pool, Id evrid)
2117 {
2118   const char *p, *evr = id2str(pool, evrid);
2119   if (!evr)
2120     return evr;
2121   for (p = evr; *p >= '0' && *p <= '9'; p++)
2122     ;
2123   return p != evr && *p == ':' ? p + 1 : evr;
2124 }
2125
2126 void
2127 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2128 {
2129   Pool *pool = data->repo->pool;
2130   Solvable *s;
2131   const char *str, *fp;
2132   int l = 0;
2133
2134   if (medianr)
2135     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2136   if (!dir)
2137     {
2138       if ((dir = strrchr(file, '/')) != 0)
2139         {
2140           l = dir - file;
2141           dir = file;
2142           file = dir + l + 1;
2143           if (!l)
2144             l++;
2145         }
2146     }
2147   else
2148     l = strlen(dir);
2149   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2150     {
2151       dir += 2;
2152       l -= 2;
2153     }
2154   if (l == 1 && dir[0] == '.')
2155     l = 0;
2156   s = pool->solvables + solvid;
2157   if (dir && l)
2158     {
2159       str = id2str(pool, s->arch);
2160       if (!strncmp(dir, str, l) && !str[l])
2161         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2162       else if (!dir[l])
2163         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
2164       else
2165         {
2166           char *dir2 = strdup(dir);
2167           dir2[l] = 0;
2168           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
2169           free(dir2);
2170         }
2171     }
2172   fp = file;
2173   str = id2str(pool, s->name);
2174   l = strlen(str);
2175   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2176     {
2177       fp += l + 1;
2178       str = evrid2vrstr(pool, s->evr);
2179       l = strlen(str);
2180       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2181         {
2182           fp += l + 1;
2183           str = id2str(pool, s->arch);
2184           l = strlen(str);
2185           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2186             {
2187               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2188               return;
2189             }
2190         }
2191     }
2192   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2193 }
2194
2195 void
2196 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2197 {
2198   assert(dir);
2199 #if 0
2200 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2201 #endif
2202   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2203   data->attriddata[data->attriddatalen++] = dir;
2204   data->attriddata[data->attriddatalen++] = num;
2205   data->attriddata[data->attriddatalen++] = num2;
2206   data->attriddata[data->attriddatalen++] = 0;
2207 }
2208
2209 void
2210 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2211 {
2212   Id stroff;
2213   int l;
2214
2215   assert(dir);
2216   l = strlen(str) + 1;
2217   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2218   memcpy(data->attrdata + data->attrdatalen, str, l);
2219   stroff = data->attrdatalen;
2220   data->attrdatalen += l;
2221
2222 #if 0
2223 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2224 #endif
2225   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2226   data->attriddata[data->attriddatalen++] = dir;
2227   data->attriddata[data->attriddatalen++] = stroff;
2228   data->attriddata[data->attriddatalen++] = 0;
2229 }
2230
2231 void
2232 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2233 {
2234 #if 0
2235 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2236 #endif
2237   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2238   data->attriddata[data->attriddatalen++] = id;
2239   data->attriddata[data->attriddatalen++] = 0;
2240 }
2241
2242 void
2243 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2244                            const char *str)
2245 {
2246   Id id;
2247   if (data->localpool)
2248     id = stringpool_str2id(&data->spool, str, 1);
2249   else
2250     id = str2id(data->repo->pool, str, 1);
2251   repodata_add_idarray(data, solvid, keyname, id);
2252 }
2253
2254 void
2255 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2256 {
2257   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2258   data->attriddata[data->attriddatalen++] = ghandle;
2259   data->attriddata[data->attriddatalen++] = 0;
2260 }
2261
2262 void
2263 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2264 {
2265   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2266   data->attriddata[data->attriddatalen++] = ghandle;
2267   data->attriddata[data->attriddatalen++] = 0;
2268 }
2269
2270 void
2271 repodata_delete_uninternalized(Repodata *data, Id solvid, Id keyname)
2272 {
2273   Id *pp, *ap, **app;
2274   app = repodata_get_attrp(data, solvid);
2275   ap = *app;
2276   if (!ap)
2277     return;
2278   for (; *ap; ap += 2)
2279     if (data->keys[*ap].name == keyname)
2280       break;
2281   if (!*ap)
2282     return;
2283   pp = ap;
2284   ap += 2;
2285   for (; *ap; ap += 2)
2286     {
2287       if (data->keys[*ap].name == keyname)
2288         continue;
2289       *pp++ = ap[0];
2290       *pp++ = ap[1];
2291     }
2292   *pp = 0;
2293 }
2294
2295 /* add all attrs from src to dest */
2296 void
2297 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2298 {
2299   Id *keyp;
2300   if (dest == src || !(keyp = data->attrs[src - data->start]))
2301     return;
2302   for (; *keyp; keyp += 2)
2303     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2304 }
2305
2306 void
2307 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2308 {
2309   Id *keyp;
2310   if (dest == src || !(keyp = data->attrs[src - data->start]))
2311     return;
2312   for (; *keyp; keyp += 2)
2313     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2314       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2315 }
2316
2317
2318
2319 /**********************************************************************/
2320
2321 /* TODO: unify with repo_write and repo_solv! */
2322
2323 #define EXTDATA_BLOCK 1023
2324
2325 struct extdata {
2326   unsigned char *buf;
2327   int len;
2328 };
2329
2330 static void
2331 data_addid(struct extdata *xd, Id x)
2332 {
2333   unsigned char *dp;
2334
2335   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2336   dp = xd->buf + xd->len;
2337
2338   if (x >= (1 << 14))
2339     {
2340       if (x >= (1 << 28))
2341         *dp++ = (x >> 28) | 128;
2342       if (x >= (1 << 21))
2343         *dp++ = (x >> 21) | 128;
2344       *dp++ = (x >> 14) | 128;
2345     }
2346   if (x >= (1 << 7))
2347     *dp++ = (x >> 7) | 128;
2348   *dp++ = x & 127;
2349   xd->len = dp - xd->buf;
2350 }
2351
2352 static void
2353 data_addideof(struct extdata *xd, Id x, int eof)
2354 {
2355   if (x >= 64)
2356     x = (x & 63) | ((x & ~63) << 1);
2357   data_addid(xd, (eof ? x : x | 64));
2358 }
2359
2360 static void
2361 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2362 {
2363   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2364   memcpy(xd->buf + xd->len, blob, len);
2365   xd->len += len;
2366 }
2367
2368 /*********************************/
2369
2370 static void
2371 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2372                        struct extdata *newvincore,
2373                        Id *schema,
2374                        Repokey *key, Id val)
2375 {
2376   /* Otherwise we have a new value.  Parse it into the internal
2377      form.  */
2378   Id *ida;
2379   struct extdata *xd;
2380   unsigned int oldvincorelen = 0;
2381   Id schemaid, *sp;
2382
2383   xd = newincore;
2384   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2385     {
2386       xd = newvincore;
2387       oldvincorelen = xd->len;
2388     }
2389   switch (key->type)
2390     {
2391     case REPOKEY_TYPE_VOID:
2392     case REPOKEY_TYPE_CONSTANT:
2393     case REPOKEY_TYPE_CONSTANTID:
2394       break;
2395     case REPOKEY_TYPE_STR:
2396       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2397       break;
2398     case REPOKEY_TYPE_MD5:
2399       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2400       break;
2401     case REPOKEY_TYPE_SHA1:
2402       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2403       break;
2404     case REPOKEY_TYPE_SHA256:
2405       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2406       break;
2407     case REPOKEY_TYPE_ID:
2408     case REPOKEY_TYPE_NUM:
2409     case REPOKEY_TYPE_DIR:
2410       data_addid(xd, val);
2411       break;
2412     case REPOKEY_TYPE_BINARY:
2413       {
2414         Id len;
2415         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2416         dp += len;
2417         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2418       }
2419       break;
2420     case REPOKEY_TYPE_IDARRAY:
2421       for (ida = data->attriddata + val; *ida; ida++)
2422         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2423       break;
2424     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2425       for (ida = data->attriddata + val; *ida; ida += 3)
2426         {
2427           data_addid(xd, ida[0]);
2428           data_addid(xd, ida[1]);
2429           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2430         }
2431       break;
2432     case REPOKEY_TYPE_DIRSTRARRAY:
2433       for (ida = data->attriddata + val; *ida; ida += 2)
2434         {
2435           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2436           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2437         }
2438       break;
2439     case REPOKEY_TYPE_FIXARRAY:
2440       {
2441         int num = 0;
2442         schemaid = 0;
2443         for (ida = data->attriddata + val; *ida; ida++)
2444           {
2445 #if 0
2446             fprintf(stderr, "serialize struct %d\n", *ida);
2447 #endif
2448             sp = schema;
2449             Id *kp = data->xattrs[-*ida];
2450             if (!kp)
2451               continue;
2452             num++;
2453             for (;*kp; kp += 2)
2454               {
2455 #if 0
2456                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2457 #endif
2458                 *sp++ = *kp;
2459               }
2460             *sp = 0;
2461             if (!schemaid)
2462               schemaid = repodata_schema2id(data, schema, 1);
2463             else if (schemaid != repodata_schema2id(data, schema, 0))
2464               {
2465                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
2466                 exit(1);
2467               }
2468 #if 0
2469             fprintf(stderr, "  schema %d\n", schemaid);
2470 #endif
2471           }
2472         if (!num)
2473           break;
2474         data_addid(xd, num);
2475         data_addid(xd, schemaid);
2476         for (ida = data->attriddata + val; *ida; ida++)
2477           {
2478             Id *kp = data->xattrs[-*ida];
2479             if (!kp)
2480               continue;
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     case REPOKEY_TYPE_FLEXARRAY:
2490       {
2491         int num = 0;
2492         for (ida = data->attriddata + val; *ida; ida++)
2493           num++;
2494         data_addid(xd, num);
2495         for (ida = data->attriddata + val; *ida; ida++)
2496           {
2497             Id *kp = data->xattrs[-*ida];
2498             if (!kp)
2499               {
2500                 data_addid(xd, 0);      /* XXX */
2501                 continue;
2502               }
2503             sp = schema;
2504             for (;*kp; kp += 2)
2505               *sp++ = *kp;
2506             *sp = 0;
2507             schemaid = repodata_schema2id(data, schema, 1);
2508             data_addid(xd, schemaid);
2509             kp = data->xattrs[-*ida];
2510             for (;*kp; kp += 2)
2511               {
2512                 repodata_serialize_key(data, newincore, newvincore,
2513                                        schema, data->keys + *kp, kp[1]);
2514               }
2515           }
2516         break;
2517       }
2518     default:
2519       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
2520       exit(1);
2521     }
2522   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2523     {
2524       /* put offset/len in incore */
2525       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2526       oldvincorelen = xd->len - oldvincorelen;
2527       data_addid(newincore, oldvincorelen);
2528     }
2529 }
2530
2531 void
2532 repodata_internalize(Repodata *data)
2533 {
2534   Repokey *key, solvkey;
2535   Id entry, nentry;
2536   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2537   unsigned char *dp, *ndp;
2538   int newschema, oldcount;
2539   struct extdata newincore;
2540   struct extdata newvincore;
2541   Id solvkeyid;
2542
2543   if (!data->attrs && !data->xattrs)
2544     return;
2545
2546   newvincore.buf = data->vincore;
2547   newvincore.len = data->vincorelen;
2548
2549   /* find the solvables key, create if needed */
2550   memset(&solvkey, 0, sizeof(solvkey));
2551   solvkey.name = REPOSITORY_SOLVABLES;
2552   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2553   solvkey.size = 0;
2554   solvkey.storage = KEY_STORAGE_INCORE;
2555   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2556
2557   schema = sat_malloc2(data->nkeys, sizeof(Id));
2558   seen = sat_malloc2(data->nkeys, sizeof(Id));
2559
2560   /* Merge the data already existing (in data->schemata, ->incoredata and
2561      friends) with the new attributes in data->attrs[].  */
2562   nentry = data->end - data->start;
2563   memset(&newincore, 0, sizeof(newincore));
2564   data_addid(&newincore, 0);    /* start data at offset 1 */
2565
2566   data->mainschema = 0;
2567   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2568
2569   /* join entry data */
2570   /* we start with the meta data, entry -1 */
2571   for (entry = -1; entry < nentry; entry++)
2572     {
2573       memset(seen, 0, data->nkeys * sizeof(Id));
2574       oldschema = 0;
2575       dp = data->incoredata;
2576       if (dp)
2577         {
2578           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2579           dp = data_read_id(dp, &oldschema);
2580         }
2581 #if 0
2582 fprintf(stderr, "oldschema %d\n", oldschema);
2583 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2584 fprintf(stderr, "schemadata %p\n", data->schemadata);
2585 #endif
2586       /* seen: -1: old data  0: skipped  >0: id + 1 */
2587       newschema = 0;
2588       oldcount = 0;
2589       sp = schema;
2590       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2591         {
2592           if (seen[*keyp])
2593             {
2594               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
2595               exit(1);
2596             }
2597           seen[*keyp] = -1;
2598           *sp++ = *keyp;
2599           oldcount++;
2600         }
2601       if (entry >= 0)
2602         keyp = data->attrs ? data->attrs[entry] : 0;
2603       else
2604         {
2605           /* strip solvables key */
2606           *sp = 0;
2607           for (sp = keyp = schema; *sp; sp++)
2608             if (*sp != solvkeyid)
2609               *keyp++ = *sp;
2610             else
2611               oldcount--;
2612           sp = keyp;
2613           seen[solvkeyid] = 0;
2614           keyp = data->xattrs ? data->xattrs[1] : 0;
2615         }
2616       if (keyp)
2617         for (; *keyp; keyp += 2)
2618           {
2619             if (!seen[*keyp])
2620               {
2621                 newschema = 1;
2622                 *sp++ = *keyp;
2623               }
2624             seen[*keyp] = keyp[1] + 1;
2625           }
2626       if (entry < 0 && data->end != data->start)
2627         {
2628           *sp++ = solvkeyid;
2629           newschema = 1;
2630         }
2631       *sp = 0;
2632       if (newschema)
2633         /* Ideally we'd like to sort the new schema here, to ensure
2634            schema equality independend of the ordering.  We can't do that
2635            yet.  For once see below (old ids need to come before new ids).
2636            An additional difficulty is that we also need to move
2637            the values with the keys.  */
2638         schemaid = repodata_schema2id(data, schema, 1);
2639       else
2640         schemaid = oldschema;
2641
2642
2643       /* Now create data blob.  We walk through the (possibly new) schema
2644          and either copy over old data, or insert the new.  */
2645       /* XXX Here we rely on the fact that the (new) schema has the form
2646          o1 o2 o3 o4 ... | n1 n2 n3 ...
2647          (oX being the old keyids (possibly overwritten), and nX being
2648           the new keyids).  This rules out sorting the keyids in order
2649          to ensure a small schema count.  */
2650       if (entry >= 0)
2651         data->incoreoffset[entry] = newincore.len;
2652       data_addid(&newincore, schemaid);
2653       if (entry == -1)
2654         {
2655           data->mainschema = schemaid;
2656           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2657         }
2658       keypstart = data->schemadata + data->schemata[schemaid];
2659       for (keyp = keypstart; *keyp; keyp++)
2660         {
2661           if (entry == -1)
2662             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2663           if (*keyp == solvkeyid)
2664             {
2665               /* add flexarray entry count */
2666               data_addid(&newincore, data->end - data->start);
2667               break;
2668             }
2669           key = data->keys + *keyp;
2670 #if 0
2671           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2672 #endif
2673           ndp = dp;
2674           if (oldcount)
2675             {
2676               /* Skip the data associated with this old key.  */
2677               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2678                 {
2679                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2680                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2681                 }
2682               else if (key->storage == KEY_STORAGE_INCORE)
2683                 ndp = data_skip_key(data, dp, key);
2684               oldcount--;
2685             }
2686           if (seen[*keyp] == -1)
2687             {
2688               /* If this key was an old one _and_ was not overwritten with
2689                  a different value copy over the old value (we skipped it
2690                  above).  */
2691               if (dp != ndp)
2692                 data_addblob(&newincore, dp, ndp - dp);
2693               seen[*keyp] = 0;
2694             }
2695           else if (seen[*keyp])
2696             {
2697               /* Otherwise we have a new value.  Parse it into the internal
2698                  form.  */
2699               repodata_serialize_key(data, &newincore, &newvincore,
2700                                      schema, key, seen[*keyp] - 1);
2701             }
2702           dp = ndp;
2703         }
2704       if (entry >= 0 && data->attrs && data->attrs[entry])
2705         data->attrs[entry] = sat_free(data->attrs[entry]);
2706     }
2707   /* free all xattrs */
2708   for (entry = 0; entry < data->nxattrs; entry++)
2709     if (data->xattrs[entry])
2710       sat_free(data->xattrs[entry]);
2711   data->xattrs = sat_free(data->xattrs);
2712   data->nxattrs = 0;
2713
2714   data->lasthandle = 0;
2715   data->lastkey = 0;
2716   data->lastdatalen = 0;
2717   sat_free(schema);
2718   sat_free(seen);
2719   repodata_free_schemahash(data);
2720
2721   sat_free(data->incoredata);
2722   data->incoredata = newincore.buf;
2723   data->incoredatalen = newincore.len;
2724   data->incoredatafree = 0;
2725
2726   sat_free(data->vincore);
2727   data->vincore = newvincore.buf;
2728   data->vincorelen = newvincore.len;
2729
2730   data->attrs = sat_free(data->attrs);
2731   data->attrdata = sat_free(data->attrdata);
2732   data->attriddata = sat_free(data->attriddata);
2733   data->attrdatalen = 0;
2734   data->attriddatalen = 0;
2735 }
2736
2737 void
2738 repodata_disable_paging(Repodata *data)
2739 {
2740   if (maybe_load_repodata(data, 0))
2741     repopagestore_disable_paging(&data->store);
2742 }
2743
2744 static void
2745 repodata_load_stub(Repodata *data)
2746 {
2747   Repo *repo = data->repo;
2748   Pool *pool = repo->pool;
2749   int r;
2750
2751   if (!pool->loadcallback)
2752     {
2753       data->state = REPODATA_ERROR;
2754       return;
2755     }
2756   data->state = REPODATA_LOADING;
2757   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
2758   if (!r)
2759     data->state = REPODATA_ERROR;
2760 }
2761
2762 void
2763 repodata_create_stubs(Repodata *data)
2764 {
2765   Repo *repo = data->repo;
2766   Pool *pool = repo->pool;
2767   Repodata *sdata;
2768   int *stubdataids;
2769   Dataiterator di;
2770   Id xkeyname = 0;
2771   int i, cnt = 0;
2772   int repodataid;
2773   int datastart, dataend;
2774
2775   repodataid = data - repo->repodata;
2776   datastart = data->start;
2777   dataend = data->end;
2778   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2779   while (dataiterator_step(&di))
2780     {
2781       if (di.data - repo->repodata != repodataid)
2782         continue;
2783       cnt++;
2784     }
2785   dataiterator_free(&di);
2786   if (!cnt)
2787     return;
2788   stubdataids = sat_calloc(cnt, sizeof(*stubdataids));
2789   for (i = 0; i < cnt; i++)
2790     {
2791       sdata = repo_add_repodata(repo, 0);
2792       if (dataend > datastart)
2793         repodata_extend_block(sdata, datastart, dataend - datastart);
2794       stubdataids[i] = sdata - repo->repodata;
2795       sdata->state = REPODATA_STUB;
2796       sdata->loadcallback = repodata_load_stub;
2797     }
2798   i = 0;
2799   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2800   sdata = 0;
2801   while (dataiterator_step(&di))
2802     {
2803       if (di.data - repo->repodata != repodataid)
2804         continue;
2805       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
2806         {
2807           dataiterator_entersub(&di);
2808           sdata = repo->repodata + stubdataids[i++];
2809           xkeyname = 0;
2810           continue;
2811         }
2812       switch (di.key->type)
2813         {
2814         case REPOKEY_TYPE_ID:
2815           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
2816           break;
2817         case REPOKEY_TYPE_CONSTANTID:
2818           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
2819           break;
2820         case REPOKEY_TYPE_STR:
2821           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
2822           break;
2823         case REPOKEY_TYPE_VOID:
2824           repodata_set_void(sdata, SOLVID_META, di.key->name);
2825           break;
2826         case REPOKEY_TYPE_NUM:
2827           repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
2828           break;
2829         case REPOKEY_TYPE_MD5:
2830         case REPOKEY_TYPE_SHA1:
2831         case REPOKEY_TYPE_SHA256:
2832           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
2833           break;
2834         case REPOKEY_TYPE_IDARRAY:
2835           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
2836           if (di.key->name == REPOSITORY_KEYS)
2837             {
2838               Repokey xkey;
2839
2840               if (!xkeyname)
2841                 {
2842                   if (!di.kv.eof)
2843                     xkeyname = di.kv.id;
2844                   continue;
2845                 }
2846               xkey.name = xkeyname;
2847               xkey.type = di.kv.id;
2848               xkey.storage = KEY_STORAGE_INCORE;
2849               xkey.size = 0; 
2850               repodata_key2id(sdata, &xkey, 1);
2851               xkeyname = 0;
2852             }
2853         }
2854     }
2855   dataiterator_free(&di);
2856   for (i = 0; i < cnt; i++)
2857     repodata_internalize(repo->repodata + stubdataids[i]);
2858   sat_free(stubdataids);
2859 }
2860
2861 /*
2862 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2863 */