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