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