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