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