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