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