- speed up file list parsing a bit
[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, 1);
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 63
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 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
1944  * so that the caller can append the new element there */
1945 static void
1946 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1947 {
1948   int oldsize;
1949   Id *ida, *pp, **ppp;
1950
1951   /* check if it is the same as last time, this speeds things up a lot */
1952   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1953     {
1954       /* great! just append the new data */
1955       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1956       data->attriddatalen--;    /* overwrite terminating 0  */
1957       data->lastdatalen += entrysize;
1958       return;
1959     }
1960
1961   ppp = repodata_get_attrp(data, handle);
1962   pp = *ppp;
1963   if (pp)
1964     for (; *pp; pp += 2)
1965       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1966         break;
1967   if (!pp || !*pp)
1968     {
1969       /* not found. allocate new key */
1970       Repokey key;
1971       key.name = keyname;
1972       key.type = keytype;
1973       key.size = 0;
1974       key.storage = KEY_STORAGE_INCORE;
1975       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1976       repodata_set(data, handle, &key, data->attriddatalen);
1977       data->lasthandle = 0;     /* next time... */
1978       return;
1979     }
1980   oldsize = 0;
1981   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1982     oldsize += entrysize;
1983   if (ida + 1 == data->attriddata + data->attriddatalen)
1984     {
1985       /* this was the last entry, just append it */
1986       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1987       data->attriddatalen--;    /* overwrite terminating 0  */
1988     }
1989   else
1990     {
1991       /* too bad. move to back. */
1992       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1993       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1994       pp[1] = data->attriddatalen;
1995       data->attriddatalen += oldsize;
1996     }
1997   data->lasthandle = handle;
1998   data->lastkey = *pp;
1999   data->lastdatalen = data->attriddatalen + entrysize + 1;
2000 }
2001
2002 void
2003 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2004                       const unsigned char *str)
2005 {
2006   Repokey key;
2007   int l;
2008
2009   if (!(l = sat_chksum_len(type)))
2010     return;
2011   key.name = keyname;
2012   key.type = type;
2013   key.size = 0;
2014   key.storage = KEY_STORAGE_INCORE;
2015   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2016   memcpy(data->attrdata + data->attrdatalen, str, l);
2017   repodata_set(data, solvid, &key, data->attrdatalen);
2018   data->attrdatalen += l;
2019 }
2020
2021 static int
2022 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2023 {
2024   int i;
2025   for (i = 0; i < buflen; i++)
2026     {
2027 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
2028                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
2029                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
2030                 : -1)
2031       int v = c2h(*str);
2032       str++;
2033       if (v < 0)
2034         return 0;
2035       buf[i] = v;
2036       v = c2h(*str);
2037       str++;
2038       if (v < 0)
2039         return 0;
2040       buf[i] = (buf[i] << 4) | v;
2041 #undef c2h
2042     }
2043   return buflen;
2044 }
2045
2046 void
2047 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2048                       const char *str)
2049 {
2050   unsigned char buf[64];
2051   int l;
2052
2053   if (!(l = sat_chksum_len(type)))
2054     return;
2055   if (hexstr2bytes(buf, str, l) != l)
2056     return;
2057   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2058 }
2059
2060 const char *
2061 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2062 {
2063   int i, l;
2064   char *str, *s;
2065
2066   if (!(l = sat_chksum_len(type)))
2067     return "";
2068   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2069   for (i = 0; i < l; i++)
2070     {
2071       unsigned char v = buf[i];
2072       unsigned char w = v >> 4;
2073       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2074       w = v & 15;
2075       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2076     }
2077   *s = 0;
2078   return str;
2079 }
2080
2081 /* rpm filenames don't contain the epoch, so strip it */
2082 static inline const char *
2083 evrid2vrstr(Pool *pool, Id evrid)
2084 {
2085   const char *p, *evr = id2str(pool, evrid);
2086   if (!evr)
2087     return evr;
2088   for (p = evr; *p >= '0' && *p <= '9'; p++)
2089     ;
2090   return p != evr && *p == ':' ? p + 1 : evr;
2091 }
2092
2093 void
2094 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2095 {
2096   Pool *pool = data->repo->pool;
2097   Solvable *s;
2098   const char *str, *fp;
2099   int l = 0;
2100
2101   if (medianr)
2102     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2103   if (!dir)
2104     {
2105       if ((dir = strrchr(file, '/')) != 0)
2106         {
2107           l = dir - file;
2108           dir = file;
2109           file = dir + l + 1;
2110           if (!l)
2111             l++;
2112         }
2113     }
2114   else
2115     l = strlen(dir);
2116   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2117     {
2118       dir += 2;
2119       l -= 2;
2120     }
2121   if (l == 1 && dir[0] == '.')
2122     l = 0;
2123   s = pool->solvables + solvid;
2124   if (dir && l)
2125     {
2126       str = id2str(pool, s->arch);
2127       if (!strncmp(dir, str, l) && !str[l])
2128         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2129       else if (!dir[l])
2130         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
2131       else
2132         {
2133           char *dir2 = strdup(dir);
2134           dir2[l] = 0;
2135           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
2136           free(dir2);
2137         }
2138     }
2139   fp = file;
2140   str = id2str(pool, s->name);
2141   l = strlen(str);
2142   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2143     {
2144       fp += l + 1;
2145       str = evrid2vrstr(pool, s->evr);
2146       l = strlen(str);
2147       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2148         {
2149           fp += l + 1;
2150           str = id2str(pool, s->arch);
2151           l = strlen(str);
2152           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2153             {
2154               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2155               return;
2156             }
2157         }
2158     }
2159   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2160 }
2161
2162 void
2163 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2164 {
2165   assert(dir);
2166 #if 0
2167 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2168 #endif
2169   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2170   data->attriddata[data->attriddatalen++] = dir;
2171   data->attriddata[data->attriddatalen++] = num;
2172   data->attriddata[data->attriddatalen++] = num2;
2173   data->attriddata[data->attriddatalen++] = 0;
2174 }
2175
2176 void
2177 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2178 {
2179   Id stroff;
2180   int l;
2181
2182   assert(dir);
2183   l = strlen(str) + 1;
2184   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2185   memcpy(data->attrdata + data->attrdatalen, str, l);
2186   stroff = data->attrdatalen;
2187   data->attrdatalen += l;
2188
2189 #if 0
2190 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2191 #endif
2192   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2193   data->attriddata[data->attriddatalen++] = dir;
2194   data->attriddata[data->attriddatalen++] = stroff;
2195   data->attriddata[data->attriddatalen++] = 0;
2196 }
2197
2198 void
2199 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2200 {
2201 #if 0
2202 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2203 #endif
2204   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2205   data->attriddata[data->attriddatalen++] = id;
2206   data->attriddata[data->attriddatalen++] = 0;
2207 }
2208
2209 void
2210 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2211                            const char *str)
2212 {
2213   Id id;
2214   if (data->localpool)
2215     id = stringpool_str2id(&data->spool, str, 1);
2216   else
2217     id = str2id(data->repo->pool, str, 1);
2218   repodata_add_idarray(data, solvid, keyname, id);
2219 }
2220
2221 void
2222 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2223 {
2224   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2225   data->attriddata[data->attriddatalen++] = ghandle;
2226   data->attriddata[data->attriddatalen++] = 0;
2227 }
2228
2229 void
2230 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2231 {
2232   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2233   data->attriddata[data->attriddatalen++] = ghandle;
2234   data->attriddata[data->attriddatalen++] = 0;
2235 }
2236
2237 /* add all attrs from src to dest */
2238 void
2239 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2240 {
2241   Id *keyp;
2242   if (dest == src || !(keyp = data->attrs[src - data->start]))
2243     return;
2244   for (; *keyp; keyp += 2)
2245     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2246 }
2247
2248 void
2249 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2250 {
2251   Id *keyp;
2252   if (dest == src || !(keyp = data->attrs[src - data->start]))
2253     return;
2254   for (; *keyp; keyp += 2)
2255     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2256       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2257 }
2258
2259
2260
2261 /**********************************************************************/
2262
2263 /* TODO: unify with repo_write! */
2264
2265 #define EXTDATA_BLOCK 1023
2266
2267 struct extdata {
2268   unsigned char *buf;
2269   int len;
2270 };
2271
2272 static void
2273 data_addid(struct extdata *xd, Id x)
2274 {
2275   unsigned char *dp;
2276   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2277   dp = xd->buf + xd->len;
2278
2279   if (x >= (1 << 14))
2280     {
2281       if (x >= (1 << 28))
2282         *dp++ = (x >> 28) | 128;
2283       if (x >= (1 << 21))
2284         *dp++ = (x >> 21) | 128;
2285       *dp++ = (x >> 14) | 128;
2286     }
2287   if (x >= (1 << 7))
2288     *dp++ = (x >> 7) | 128;
2289   *dp++ = x & 127;
2290   xd->len = dp - xd->buf;
2291 }
2292
2293 static void
2294 data_addideof(struct extdata *xd, Id x, int eof)
2295 {
2296   if (x >= 64)
2297     x = (x & 63) | ((x & ~63) << 1);
2298   data_addid(xd, (eof ? x: x | 64));
2299 }
2300
2301 static void
2302 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2303 {
2304   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2305   memcpy(xd->buf + xd->len, blob, len);
2306   xd->len += len;
2307 }
2308
2309 /*********************************/
2310
2311 static void
2312 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2313                        struct extdata *newvincore,
2314                        Id *schema,
2315                        Repokey *key, Id val)
2316 {
2317   /* Otherwise we have a new value.  Parse it into the internal
2318      form.  */
2319   Id *ida;
2320   struct extdata *xd;
2321   unsigned int oldvincorelen = 0;
2322   Id schemaid, *sp;
2323
2324   xd = newincore;
2325   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2326     {
2327       xd = newvincore;
2328       oldvincorelen = xd->len;
2329     }
2330   switch (key->type)
2331     {
2332     case REPOKEY_TYPE_VOID:
2333     case REPOKEY_TYPE_CONSTANT:
2334     case REPOKEY_TYPE_CONSTANTID:
2335       break;
2336     case REPOKEY_TYPE_STR:
2337       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2338       break;
2339     case REPOKEY_TYPE_MD5:
2340       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2341       break;
2342     case REPOKEY_TYPE_SHA1:
2343       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2344       break;
2345     case REPOKEY_TYPE_SHA256:
2346       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2347       break;
2348     case REPOKEY_TYPE_ID:
2349     case REPOKEY_TYPE_NUM:
2350     case REPOKEY_TYPE_DIR:
2351       data_addid(xd, val);
2352       break;
2353     case REPOKEY_TYPE_IDARRAY:
2354       for (ida = data->attriddata + val; *ida; ida++)
2355         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2356       break;
2357     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2358       for (ida = data->attriddata + val; *ida; ida += 3)
2359         {
2360           data_addid(xd, ida[0]);
2361           data_addid(xd, ida[1]);
2362           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2363         }
2364       break;
2365     case REPOKEY_TYPE_DIRSTRARRAY:
2366       for (ida = data->attriddata + val; *ida; ida += 2)
2367         {
2368           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2369           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2370         }
2371       break;
2372     case REPOKEY_TYPE_FIXARRAY:
2373       {
2374         int num = 0;
2375         schemaid = 0;
2376         for (ida = data->attriddata + val; *ida; ida++)
2377           {
2378 #if 0
2379             fprintf(stderr, "serialize struct %d\n", *ida);
2380 #endif
2381             sp = schema;
2382             Id *kp = data->xattrs[-*ida];
2383             if (!kp)
2384               continue;
2385             num++;
2386             for (;*kp; kp += 2)
2387               {
2388 #if 0
2389                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2390 #endif
2391                 *sp++ = *kp;
2392               }
2393             *sp = 0;
2394             if (!schemaid)
2395               schemaid = repodata_schema2id(data, schema, 1);
2396             else if (schemaid != repodata_schema2id(data, schema, 0))
2397               {
2398                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
2399                 exit(1);
2400               }
2401 #if 0
2402             fprintf(stderr, "  schema %d\n", schemaid);
2403 #endif
2404           }
2405         if (!num)
2406           break;
2407         data_addid(xd, num);
2408         data_addid(xd, schemaid);
2409         for (ida = data->attriddata + val; *ida; ida++)
2410           {
2411             Id *kp = data->xattrs[-*ida];
2412             if (!kp)
2413               continue;
2414             for (;*kp; kp += 2)
2415               {
2416                 repodata_serialize_key(data, newincore, newvincore,
2417                                        schema, data->keys + *kp, kp[1]);
2418               }
2419           }
2420         break;
2421       }
2422     case REPOKEY_TYPE_FLEXARRAY:
2423       {
2424         int num = 0;
2425         for (ida = data->attriddata + val; *ida; ida++)
2426           num++;
2427         data_addid(xd, num);
2428         for (ida = data->attriddata + val; *ida; ida++)
2429           {
2430             Id *kp = data->xattrs[-*ida];
2431             if (!kp)
2432               {
2433                 data_addid(xd, 0);      /* XXX */
2434                 continue;
2435               }
2436             sp = schema;
2437             for (;*kp; kp += 2)
2438               *sp++ = *kp;
2439             *sp = 0;
2440             schemaid = repodata_schema2id(data, schema, 1);
2441             data_addid(xd, schemaid);
2442             kp = data->xattrs[-*ida];
2443             for (;*kp; kp += 2)
2444               {
2445                 repodata_serialize_key(data, newincore, newvincore,
2446                                        schema, data->keys + *kp, kp[1]);
2447               }
2448           }
2449         break;
2450       }
2451     default:
2452       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
2453       exit(1);
2454     }
2455   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2456     {
2457       /* put offset/len in incore */
2458       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2459       oldvincorelen = xd->len - oldvincorelen;
2460       data_addid(newincore, oldvincorelen);
2461     }
2462 }
2463
2464 void
2465 repodata_internalize(Repodata *data)
2466 {
2467   Repokey *key, solvkey;
2468   Id entry, nentry;
2469   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2470   unsigned char *dp, *ndp;
2471   int newschema, oldcount;
2472   struct extdata newincore;
2473   struct extdata newvincore;
2474   Id solvkeyid;
2475
2476   if (!data->attrs && !data->xattrs)
2477     return;
2478
2479   newvincore.buf = data->vincore;
2480   newvincore.len = data->vincorelen;
2481
2482   /* find the solvables key, create if needed */
2483   memset(&solvkey, 0, sizeof(solvkey));
2484   solvkey.name = REPOSITORY_SOLVABLES;
2485   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2486   solvkey.size = 0;
2487   solvkey.storage = KEY_STORAGE_INCORE;
2488   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2489
2490   schema = sat_malloc2(data->nkeys, sizeof(Id));
2491   seen = sat_malloc2(data->nkeys, sizeof(Id));
2492
2493   /* Merge the data already existing (in data->schemata, ->incoredata and
2494      friends) with the new attributes in data->attrs[].  */
2495   nentry = data->end - data->start;
2496   memset(&newincore, 0, sizeof(newincore));
2497   data_addid(&newincore, 0);    /* start data at offset 1 */
2498
2499   data->mainschema = 0;
2500   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2501
2502   /* join entry data */
2503   /* we start with the meta data, entry -1 */
2504   for (entry = -1; entry < nentry; entry++)
2505     {
2506       memset(seen, 0, data->nkeys * sizeof(Id));
2507       oldschema = 0;
2508       dp = data->incoredata;
2509       if (dp)
2510         {
2511           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2512           dp = data_read_id(dp, &oldschema);
2513         }
2514 #if 0
2515 fprintf(stderr, "oldschema %d\n", oldschema);
2516 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2517 fprintf(stderr, "schemadata %p\n", data->schemadata);
2518 #endif
2519       /* seen: -1: old data  0: skipped  >0: id + 1 */
2520       newschema = 0;
2521       oldcount = 0;
2522       sp = schema;
2523       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2524         {
2525           if (seen[*keyp])
2526             {
2527               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
2528               exit(1);
2529             }
2530           seen[*keyp] = -1;
2531           *sp++ = *keyp;
2532           oldcount++;
2533         }
2534       if (entry >= 0)
2535         keyp = data->attrs ? data->attrs[entry] : 0;
2536       else
2537         {
2538           /* strip solvables key */
2539           *sp = 0;
2540           for (sp = keyp = schema; *sp; sp++)
2541             if (*sp != solvkeyid)
2542               *keyp++ = *sp;
2543             else
2544               oldcount--;
2545           sp = keyp;
2546           seen[solvkeyid] = 0;
2547           keyp = data->xattrs ? data->xattrs[1] : 0;
2548         }
2549       if (keyp)
2550         for (; *keyp; keyp += 2)
2551           {
2552             if (!seen[*keyp])
2553               {
2554                 newschema = 1;
2555                 *sp++ = *keyp;
2556               }
2557             seen[*keyp] = keyp[1] + 1;
2558           }
2559       if (entry < 0 && data->end != data->start)
2560         {
2561           *sp++ = solvkeyid;
2562           newschema = 1;
2563         }
2564       *sp = 0;
2565       if (newschema)
2566         /* Ideally we'd like to sort the new schema here, to ensure
2567            schema equality independend of the ordering.  We can't do that
2568            yet.  For once see below (old ids need to come before new ids).
2569            An additional difficulty is that we also need to move
2570            the values with the keys.  */
2571         schemaid = repodata_schema2id(data, schema, 1);
2572       else
2573         schemaid = oldschema;
2574
2575
2576       /* Now create data blob.  We walk through the (possibly new) schema
2577          and either copy over old data, or insert the new.  */
2578       /* XXX Here we rely on the fact that the (new) schema has the form
2579          o1 o2 o3 o4 ... | n1 n2 n3 ...
2580          (oX being the old keyids (possibly overwritten), and nX being
2581           the new keyids).  This rules out sorting the keyids in order
2582          to ensure a small schema count.  */
2583       if (entry >= 0)
2584         data->incoreoffset[entry] = newincore.len;
2585       data_addid(&newincore, schemaid);
2586       if (entry == -1)
2587         {
2588           data->mainschema = schemaid;
2589           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2590         }
2591       keypstart = data->schemadata + data->schemata[schemaid];
2592       for (keyp = keypstart; *keyp; keyp++)
2593         {
2594           if (entry == -1)
2595             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2596           if (*keyp == solvkeyid)
2597             {
2598               /* add flexarray entry count */
2599               data_addid(&newincore, data->end - data->start);
2600               break;
2601             }
2602           key = data->keys + *keyp;
2603 #if 0
2604           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2605 #endif
2606           ndp = dp;
2607           if (oldcount)
2608             {
2609               /* Skip the data associated with this old key.  */
2610               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2611                 {
2612                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2613                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2614                 }
2615               else if (key->storage == KEY_STORAGE_INCORE)
2616                 ndp = data_skip_key(data, dp, key);
2617               oldcount--;
2618             }
2619           if (seen[*keyp] == -1)
2620             {
2621               /* If this key was an old one _and_ was not overwritten with
2622                  a different value copy over the old value (we skipped it
2623                  above).  */
2624               if (dp != ndp)
2625                 data_addblob(&newincore, dp, ndp - dp);
2626               seen[*keyp] = 0;
2627             }
2628           else if (seen[*keyp])
2629             {
2630               /* Otherwise we have a new value.  Parse it into the internal
2631                  form.  */
2632               repodata_serialize_key(data, &newincore, &newvincore,
2633                                      schema, key, seen[*keyp] - 1);
2634             }
2635           dp = ndp;
2636         }
2637       if (entry >= 0 && data->attrs && data->attrs[entry])
2638         data->attrs[entry] = sat_free(data->attrs[entry]);
2639     }
2640   /* free all xattrs */
2641   for (entry = 0; entry < data->nxattrs; entry++)
2642     if (data->xattrs[entry])
2643       sat_free(data->xattrs[entry]);
2644   data->xattrs = sat_free(data->xattrs);
2645   data->nxattrs = 0;
2646
2647   data->lasthandle = 0;
2648   data->lastkey = 0;
2649   data->lastdatalen = 0;
2650   sat_free(schema);
2651   sat_free(seen);
2652   repodata_free_schemahash(data);
2653
2654   sat_free(data->incoredata);
2655   data->incoredata = newincore.buf;
2656   data->incoredatalen = newincore.len;
2657   data->incoredatafree = 0;
2658
2659   sat_free(data->vincore);
2660   data->vincore = newvincore.buf;
2661   data->vincorelen = newvincore.len;
2662
2663   data->attrs = sat_free(data->attrs);
2664   data->attrdata = sat_free(data->attrdata);
2665   data->attriddata = sat_free(data->attriddata);
2666   data->attrdatalen = 0;
2667   data->attriddatalen = 0;
2668 }
2669
2670 void
2671 repodata_disable_paging(Repodata *data)
2672 {
2673   if (maybe_load_repodata(data, 0))
2674     repopagestore_disable_paging(&data->store);
2675 }
2676
2677 static void
2678 repodata_load_stub(Repodata *data)
2679 {
2680   Repo *repo = data->repo;
2681   Pool *pool = repo->pool;
2682   int r;
2683
2684   if (!pool->loadcallback)
2685     {
2686       data->state = REPODATA_ERROR;
2687       return;
2688     }
2689   data->state = REPODATA_LOADING;
2690   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
2691   if (!r)
2692     data->state = REPODATA_ERROR;
2693 }
2694
2695 void
2696 repodata_create_stubs(Repodata *data)
2697 {
2698   Repo *repo = data->repo;
2699   Pool *pool = repo->pool;
2700   Repodata *sdata;
2701   int *stubdataids;
2702   Dataiterator di;
2703   Id xkeyname = 0;
2704   int i, cnt = 0;
2705   int repodataid;
2706   int datastart, dataend;
2707
2708   repodataid = data - repo->repodata;
2709   datastart = data->start;
2710   dataend = data->end;
2711   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2712   while (dataiterator_step(&di))
2713     {
2714       if (di.data - repo->repodata != repodataid)
2715         continue;
2716       cnt++;
2717     }
2718   dataiterator_free(&di);
2719   if (!cnt)
2720     return;
2721   stubdataids = sat_calloc(cnt, sizeof(*stubdataids));
2722   for (i = 0; i < cnt; i++)
2723     {
2724       sdata = repo_add_repodata(repo, 0);
2725       if (dataend > datastart)
2726         repodata_extend_block(sdata, datastart, dataend - datastart);
2727       stubdataids[i] = sdata - repo->repodata;
2728       sdata->state = REPODATA_STUB;
2729       sdata->loadcallback = repodata_load_stub;
2730     }
2731   i = 0;
2732   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2733   sdata = 0;
2734   while (dataiterator_step(&di))
2735     {
2736       if (di.data - repo->repodata != repodataid)
2737         continue;
2738       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
2739         {
2740           dataiterator_entersub(&di);
2741           sdata = repo->repodata + stubdataids[i++];
2742           xkeyname = 0;
2743           continue;
2744         }
2745       switch (di.key->type)
2746         {
2747         case REPOKEY_TYPE_ID:
2748           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
2749           break;
2750         case REPOKEY_TYPE_CONSTANTID:
2751           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
2752           break;
2753         case REPOKEY_TYPE_STR:
2754           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
2755           break;
2756         case REPOKEY_TYPE_VOID:
2757           repodata_set_void(sdata, SOLVID_META, di.key->name);
2758           break;
2759         case REPOKEY_TYPE_NUM:
2760           repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
2761           break;
2762         case REPOKEY_TYPE_MD5:
2763         case REPOKEY_TYPE_SHA1:
2764         case REPOKEY_TYPE_SHA256:
2765           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
2766           break;
2767         case REPOKEY_TYPE_IDARRAY:
2768           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
2769           if (di.key->name == REPOSITORY_KEYS)
2770             {
2771               Repokey xkey;
2772
2773               if (!xkeyname)
2774                 {
2775                   if (!di.kv.eof)
2776                     xkeyname = di.kv.id;
2777                   continue;
2778                 }
2779               xkey.name = xkeyname;
2780               xkey.type = di.kv.id;
2781               xkey.storage = KEY_STORAGE_INCORE;
2782               xkey.size = 0; 
2783               repodata_key2id(sdata, &xkey, 1);
2784               xkeyname = 0;
2785             }
2786         }
2787     }
2788   dataiterator_free(&di);
2789   for (i = 0; i < cnt; i++)
2790     repodata_internalize(repo->repodata + stubdataids[i]);
2791   sat_free(stubdataids);
2792 }
2793
2794 /*
2795 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2796 */