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