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