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