- fix segfault on 64bit
[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  */
14
15 #define _GNU_SOURCE
16 #include <string.h>
17 #include <fnmatch.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <assert.h>
23 #include <regex.h>
24
25 #include "repo.h"
26 #include "pool.h"
27 #include "poolid_private.h"
28 #include "util.h"
29
30 #include "repopack.h"
31 #include "repopage.h"
32
33 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
34                                   unsigned char *out, unsigned int out_len);
35 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
36                                               unsigned int in_len,
37                                               unsigned char *out,
38                                               unsigned int out_len);
39
40 #define REPODATA_BLOCK 255
41
42
43 void
44 repodata_init(Repodata *data, Repo *repo, int localpool)
45 {
46   memset(data, 0, sizeof (*data));
47   data->repo = repo;
48   data->localpool = localpool;
49   if (localpool)
50     stringpool_init_empty(&data->spool);
51   data->keys = sat_calloc(1, sizeof(Repokey));
52   data->nkeys = 1;
53   data->schemata = sat_calloc(1, sizeof(Id));
54   data->schemadata = sat_calloc(1, sizeof(Id));
55   data->nschemata = 1;
56   data->schemadatalen = 1;
57   repopagestore_init(&data->store);
58 }
59
60 void
61 repodata_free(Repodata *data)
62 {
63   int i;
64
65   sat_free(data->keys);
66
67   sat_free(data->schemata);
68   sat_free(data->schemadata);
69   sat_free(data->schematahash);
70
71   stringpool_free(&data->spool);
72   dirpool_free(&data->dirpool);
73
74   sat_free(data->mainschemaoffsets);
75   sat_free(data->incoredata);
76   sat_free(data->incoreoffset);
77   sat_free(data->verticaloffset);
78
79   repopagestore_free(&data->store);
80
81   sat_free(data->vincore);
82
83   if (data->attrs)
84     for (i = 0; i < data->end - data->start; i++)
85       sat_free(data->attrs[i]);
86   sat_free(data->attrs);
87   if (data->xattrs)
88     for (i = 0; i < data->nxattrs; i++)
89       sat_free(data->xattrs[i]);
90   sat_free(data->xattrs);
91
92   sat_free(data->attrdata);
93   sat_free(data->attriddata);
94 }
95
96
97 /***************************************************************
98  * key pool management
99  */
100
101 /* this is not so time critical that we need a hash, so we do a simple
102  * linear search */
103 Id
104 repodata_key2id(Repodata *data, Repokey *key, int create)
105 {
106   Id keyid;
107
108   for (keyid = 1; keyid < data->nkeys; keyid++)
109     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
110       {
111         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
112           continue;
113         break;
114       }
115   if (keyid == data->nkeys)
116     {
117       if (!create)
118         return 0;
119       /* allocate new key */
120       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
121       data->keys[data->nkeys++] = *key;
122       if (data->verticaloffset)
123         {
124           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
125           data->verticaloffset[data->nkeys - 1] = 0;
126         }
127       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
128     }
129   return keyid;
130 }
131
132
133 /***************************************************************
134  * schema pool management
135  */
136
137 #define SCHEMATA_BLOCK 31
138 #define SCHEMATADATA_BLOCK 255
139
140 Id
141 repodata_schema2id(Repodata *data, Id *schema, int create)
142 {
143   int h, len, i;
144   Id *sp, cid;
145   Id *schematahash;
146
147   if ((schematahash = data->schematahash) == 0)
148     {
149       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
150       for (i = 0; i < data->nschemata; i++)
151         {
152           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
153             h = h * 7 + *sp++;
154           h &= 255;
155           schematahash[h] = i + 1;
156         }
157       data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
158       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
159     }
160
161   for (sp = schema, len = 0, h = 0; *sp; len++)
162     h = h * 7 + *sp++;
163   h &= 255;
164   len++;
165
166   cid = schematahash[h];
167   if (cid)
168     {
169       cid--;
170       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
171         return cid;
172       /* cache conflict */
173       for (cid = 0; cid < data->nschemata; cid++)
174         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
175           return cid;
176     }
177   /* a new one */
178   if (!create)
179     return 0;
180   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
181   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
182   /* add schema */
183   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
184   data->schemata[data->nschemata] = data->schemadatalen;
185   data->schemadatalen += len;
186   schematahash[h] = data->nschemata + 1;
187 #if 0
188 fprintf(stderr, "schema2id: new schema\n");
189 #endif
190   return data->nschemata++;
191 }
192
193 void
194 repodata_free_schemahash(Repodata *data)
195 {
196   data->schematahash = sat_free(data->schematahash);
197   /* shrink arrays */
198   data->schemata = sat_realloc2(data->schemata, data->nschemata, sizeof(Id));
199   data->schemadata = sat_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
200 }
201
202
203 /***************************************************************
204  * dir pool management
205  */
206
207 Id
208 repodata_str2dir(Repodata *data, const char *dir, int create)
209 {
210   Id id, parent;
211   const char *dire;
212
213   parent = 0;
214   while (*dir == '/' && dir[1] == '/')
215     dir++;
216   if (*dir == '/' && !dir[1])
217     return 1;
218   while (*dir)
219     {
220       dire = strchrnul(dir, '/');
221       if (data->localpool)
222         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
223       else
224         id = strn2id(data->repo->pool, dir, dire - dir, create);
225       if (!id)
226         return 0;
227       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
228       if (!parent)
229         return 0;
230       if (!*dire)
231         break;
232       dir = dire + 1;
233       while (*dir == '/')
234         dir++;
235     }
236   return parent;
237 }
238
239 const char *
240 repodata_dir2str(Repodata *data, Id did, const char *suf)
241 {
242   Pool *pool = data->repo->pool;
243   int l = 0;
244   Id parent, comp;
245   const char *comps;
246   char *p;
247
248   if (!did)
249     return suf ? suf : "";
250   parent = did;
251   while (parent)
252     {
253       comp = dirpool_compid(&data->dirpool, parent);
254       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
255       l += strlen(comps);
256       parent = dirpool_parent(&data->dirpool, parent);
257       if (parent)
258         l++;
259     }
260   if (suf)
261     l += strlen(suf) + 1;
262   p = pool_alloctmpspace(pool, l + 1) + l;
263   *p = 0;
264   if (suf)
265     {
266       p -= strlen(suf);
267       strcpy(p, suf);
268       *--p = '/';
269     }
270   parent = did;
271   while (parent)
272     {
273       comp = dirpool_compid(&data->dirpool, parent);
274       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
275       l = strlen(comps);
276       p -= l;
277       strncpy(p, comps, l);
278       parent = dirpool_parent(&data->dirpool, parent);
279       if (parent)
280         *--p = '/';
281     }
282   return p;
283 }
284
285
286 /***************************************************************
287  * data management
288  */
289
290 static inline unsigned char *
291 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
292 {
293   Id *keyp = data->schemadata + data->schemata[schema];
294   for (; *keyp; keyp++)
295     dp = data_skip_key(data, dp, data->keys + *keyp);
296   return dp;
297 }
298
299 unsigned char *
300 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
301 {
302   int nentries, schema;
303   switch(key->type)
304     {
305     case REPOKEY_TYPE_FIXARRAY:
306       dp = data_read_id(dp, &nentries);
307       if (!nentries)
308         return dp;
309       dp = data_read_id(dp, &schema);
310       while (nentries--)
311         dp = data_skip_schema(data, dp, schema);
312       return dp;
313     case REPOKEY_TYPE_FLEXARRAY:
314       dp = data_read_id(dp, &nentries);
315       while (nentries--)
316         {
317           dp = data_read_id(dp, &schema);
318           dp = data_skip_schema(data, dp, schema);
319         }
320       return dp;
321     default:
322       if (key->storage == KEY_STORAGE_INCORE)
323         dp = data_skip(dp, key->type);
324       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
325         {
326           dp = data_skip(dp, REPOKEY_TYPE_ID);
327           dp = data_skip(dp, REPOKEY_TYPE_ID);
328         }
329       return dp;
330     }
331 }
332
333 static unsigned char *
334 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
335 {
336   Id k;
337
338   if (!keyid)
339     return 0;
340   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
341     {
342       int i;
343       for (i = 0; (k = *keyp++) != 0; i++)
344         if (k == keyid)
345           return data->incoredata + data->mainschemaoffsets[i];
346       return 0;
347     }
348   while ((k = *keyp++) != 0)
349     {
350       if (k == keyid)
351         return dp;
352       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
353         {
354           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
355           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
356           continue;
357         }
358       if (data->keys[k].storage != KEY_STORAGE_INCORE)
359         continue;
360       dp = data_skip_key(data, dp, data->keys + k);
361     }
362   return 0;
363 }
364
365 static unsigned char *
366 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
367 {
368   unsigned char *dp;
369   if (!len)
370     return 0;
371   if (off >= data->lastverticaloffset)
372     {
373       off -= data->lastverticaloffset;
374       if (off + len > data->vincorelen)
375         return 0;
376       return data->vincore + off;
377     }
378   if (off + len > key->size)
379     return 0;
380   /* we now have the offset, go into vertical */
381   off += data->verticaloffset[key - data->keys];
382   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
383   dp = repopagestore_load_page_range(&data->store, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
384   if (dp)
385     dp += off % BLOB_PAGESIZE;
386   return dp;
387 }
388
389 static inline unsigned char *
390 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
391 {
392   unsigned char *dp = *dpp;
393
394   if (!dp)
395     return 0;
396   if (key->storage == KEY_STORAGE_INCORE)
397     {
398       if (advance)
399         *dpp = data_skip_key(data, dp, key);
400       return dp;
401     }
402   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
403     {
404       Id off, len;
405       dp = data_read_id(dp, &off);
406       dp = data_read_id(dp, &len);
407       if (advance)
408         *dpp = dp;
409       return get_vertical_data(data, key, off, len);
410     }
411   return 0;
412 }
413
414 static int
415 load_repodata(Repodata *data)
416 {
417   if (data->loadcallback)
418     {
419       data->loadcallback(data);
420       if (data->state == REPODATA_AVAILABLE)
421         return 1;
422     }
423   data->state = REPODATA_ERROR;
424   return 0;
425 }
426
427 static inline int
428 maybe_load_repodata(Repodata *data, Id keyname)
429 {
430   if (keyname && !repodata_precheck_keyname(data, keyname))
431     return 0;   /* do not bother... */
432   switch(data->state)
433     {
434     case REPODATA_STUB:
435       if (keyname)
436         {
437           int i;
438           for (i = 0; i < data->nkeys; i++)
439             if (keyname == data->keys[i].name)
440               break;
441           if (i == data->nkeys)
442             return 0;
443         }
444       return load_repodata(data);
445     case REPODATA_ERROR:
446       return 0;
447     case REPODATA_AVAILABLE:
448       return 1;
449     default:
450       data->state = REPODATA_ERROR;
451       return 0;
452     }
453 }
454
455 static inline unsigned char*
456 solvid2data(Repodata *data, Id solvid, Id *schemap)
457 {
458   unsigned char *dp = data->incoredata;
459   if (!dp)
460     return 0;
461   if (solvid == SOLVID_META)    /* META */
462     dp += 1;
463   else if (solvid == SOLVID_POS)        /* META */
464     {
465       Pool *pool = data->repo->pool;
466       if (data->repo != pool->pos.repo)
467         return 0;
468       if (data != data->repo->repodata + pool->pos.repodataid)
469         return 0;
470       *schemap = pool->pos.schema;
471       return data->incoredata + pool->pos.dp;
472     }
473   else
474     {
475       if (solvid < data->start || solvid >= data->end)
476         return 0;
477       dp += data->incoreoffset[solvid - data->start];
478     }
479   return data_read_id(dp, schemap);
480 }
481
482 /************************************************************************
483  * data lookup
484  */
485
486 static inline unsigned char *
487 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
488 {
489   unsigned char *dp;
490   Id schema, *keyp, *kp;
491   Repokey *key;
492
493   if (!maybe_load_repodata(data, keyname))
494     return 0;
495   dp = solvid2data(data, solvid, &schema);
496   if (!dp)
497     return 0;
498   keyp = data->schemadata + data->schemata[schema];
499   for (kp = keyp; *kp; kp++)
500     if (data->keys[*kp].name == keyname)
501       break;
502   if (!*kp)
503     return 0;
504   *keypp = key = data->keys + *kp;
505   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
506     return dp;  /* no need to forward... */
507   dp = forward_to_key(data, *kp, keyp, dp);
508   if (!dp)
509     return 0;
510   return get_data(data, key, &dp, 0);
511 }
512
513
514 Id
515 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
516 {
517   unsigned char *dp;
518   Repokey *key;
519   Id id;
520
521   dp = find_key_data(data, solvid, keyname, &key);
522   if (!dp)
523     return 0;
524   if (key->type == REPOKEY_TYPE_CONSTANTID)
525     return key->size;
526   if (key->type != REPOKEY_TYPE_ID)
527     return 0;
528   dp = data_read_id(dp, &id);
529   return id;
530 }
531
532 const char *
533 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
534 {
535   unsigned char *dp;
536   Repokey *key;
537   Id id;
538
539   dp = find_key_data(data, solvid, keyname, &key);
540   if (!dp)
541     return 0;
542   if (key->type == REPOKEY_TYPE_STR)
543     return (const char *)dp;
544   if (key->type == REPOKEY_TYPE_CONSTANTID)
545     return id2str(data->repo->pool, key->size);
546   if (key->type == REPOKEY_TYPE_ID)
547     dp = data_read_id(dp, &id);
548   else
549     return 0;
550   if (data->localpool)
551     return data->spool.stringspace + data->spool.strings[id];
552   return id2str(data->repo->pool, id);
553 }
554
555 int
556 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned int *value)
557 {
558   unsigned char *dp;
559   Repokey *key;
560   KeyValue kv;
561
562   *value = 0;
563   dp = find_key_data(data, solvid, keyname, &key);
564   if (!dp)
565     return 0;
566   if (key->type == REPOKEY_TYPE_NUM
567       || key->type == REPOKEY_TYPE_U32
568       || key->type == REPOKEY_TYPE_CONSTANT)
569     {
570       dp = data_fetch(dp, &kv, key);
571       *value = kv.num;
572       return 1;
573     }
574   return 0;
575 }
576
577 int
578 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
579 {
580   Id schema;
581   Id *keyp;
582   unsigned char *dp;
583
584   if (!maybe_load_repodata(data, keyname))
585     return 0;
586   dp = solvid2data(data, solvid, &schema);
587   if (!dp)
588     return 0;
589   /* can't use find_key_data as we need to test the type */
590   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
591     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
592       return 1;
593   return 0;
594 }
595
596 const unsigned char *
597 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
598 {
599   unsigned char *dp;
600   Repokey *key;
601
602   dp = find_key_data(data, solvid, keyname, &key);
603   if (!dp)
604     return 0;
605   *typep = key->type;
606   return dp;
607 }
608
609
610 /************************************************************************
611  * data search
612  */
613
614 struct subschema_data {
615   Solvable *s;
616   void *cbdata;
617   KeyValue *parent;
618 };
619
620 /* search a specific repodata */
621 void
622 repodata_search(Repodata *data, Id solvid, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
623 {
624   Id schema;
625   Repokey *key;
626   Id keyid, *kp, *keyp;
627   unsigned char *dp, *ddp;
628   int onekey = 0;
629   int stop;
630   KeyValue kv;
631   Solvable *s;
632
633   if (!maybe_load_repodata(data, keyname))
634     return;
635   if (solvid == SOLVID_SUBSCHEMA)
636     {
637       struct subschema_data *subd = cbdata;
638       cbdata = subd->cbdata;
639       s = subd->s;
640       schema = subd->parent->id;
641       dp = (unsigned char *)subd->parent->str;
642       kv.parent = subd->parent;
643     }
644   else
645     {
646       schema = 0;
647       dp = solvid2data(data, solvid, &schema);
648       if (!dp)
649         return;
650       s = data->repo->pool->solvables + solvid;
651       kv.parent = 0;
652     }
653   keyp = data->schemadata + data->schemata[schema];
654   if (keyname)
655     {
656       /* search for a specific key */
657       for (kp = keyp; *kp; kp++)
658         if (data->keys[*kp].name == keyname)
659           break;
660       if (!*kp)
661         return;
662       dp = forward_to_key(data, *kp, keyp, dp);
663       if (!dp)
664         return;
665       keyp = kp;
666       onekey = 1;
667     }
668   while ((keyid = *keyp++) != 0)
669     {
670       stop = 0;
671       key = data->keys + keyid;
672       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
673
674       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
675         {
676           struct subschema_data subd;
677           int nentries;
678           Id schema = 0;
679
680           subd.cbdata = cbdata;
681           subd.s = s;
682           subd.parent = &kv;
683           ddp = data_read_id(ddp, &nentries);
684           kv.num = nentries;
685           kv.entry = 0;
686           kv.eof = 0;
687           while (ddp && nentries > 0)
688             {
689               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
690                 ddp = data_read_id(ddp, &schema);
691               kv.id = schema;
692               kv.str = (char *)ddp;
693               stop = callback(cbdata, s, data, key, &kv);
694               if (stop > SEARCH_NEXT_KEY)
695                 return;
696               if (stop)
697                 break;
698               if (!keyname)
699                 repodata_search(data, SOLVID_SUBSCHEMA, 0, callback, &subd);
700               ddp = data_skip_schema(data, ddp, schema);
701               nentries--;
702               kv.entry++;
703             }
704           if (!nentries)
705             {
706               /* sentinel */
707               kv.eof = 1;
708               kv.str = (char *)ddp;
709               stop = callback(cbdata, s, data, key, &kv);
710               if (stop > SEARCH_NEXT_KEY)
711                 return;
712             }
713           if (onekey)
714             return;
715           continue;
716         }
717       kv.entry = 0;
718       do
719         {
720           ddp = data_fetch(ddp, &kv, key);
721           if (!ddp)
722             break;
723           stop = callback(cbdata, s, data, key, &kv);
724           kv.entry++;
725         }
726       while (!kv.eof && !stop);
727       if (onekey || stop > SEARCH_NEXT_KEY)
728         return;
729     }
730 }
731
732 void
733 repodata_setpos_kv(Repodata *data, KeyValue *kv)
734 {
735   Pool *pool = data->repo->pool;
736   if (!kv)
737     {
738       pool->pos.repo = 0;
739       pool->pos.repodataid = 0;
740       pool->pos.dp = 0;
741       pool->pos.schema = 0;
742     }
743   else
744     {
745       pool->pos.repo = 0;
746       pool->pos.repodataid = data - data->repo->repodata;
747       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
748       pool->pos.schema = kv->id;
749     }
750 }
751
752 /************************************************************************
753  * data iterator functions
754  */
755
756 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
757   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
758   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
759   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
760   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
761   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
762   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
763   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
764   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
765   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
766   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
767   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
768   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
769   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
770 };
771
772 static inline Id *
773 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
774 {
775   kv->id = keyname;
776   switch (keyname)
777     {
778     case SOLVABLE_NAME:
779       kv->eof = 1;
780       return &s->name;
781     case SOLVABLE_ARCH:
782       kv->eof = 1;
783       return &s->arch;
784     case SOLVABLE_EVR:
785       kv->eof = 1;
786       return &s->evr;
787     case SOLVABLE_VENDOR:
788       kv->eof = 1;
789       return &s->vendor;
790     case SOLVABLE_PROVIDES:
791       kv->eof = 0;
792       return s->provides ? s->repo->idarraydata + s->provides : 0;
793     case SOLVABLE_OBSOLETES:
794       kv->eof = 0;
795       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
796     case SOLVABLE_CONFLICTS:
797       kv->eof = 0;
798       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
799     case SOLVABLE_REQUIRES:
800       kv->eof = 0;
801       return s->requires ? s->repo->idarraydata + s->requires : 0;
802     case SOLVABLE_RECOMMENDS:
803       kv->eof = 0;
804       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
805     case SOLVABLE_SUPPLEMENTS:
806       kv->eof = 0;
807       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
808     case SOLVABLE_SUGGESTS:
809       kv->eof = 0;
810       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
811     case SOLVABLE_ENHANCES:
812       kv->eof = 0;
813       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
814     case RPM_RPMDBID:
815       kv->eof = 1;
816       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
817     default:
818       return 0;
819     }
820 }
821
822 void
823 datamatcher_init(Datamatcher *ma, Pool *pool, const char *match, int flags)
824 {
825   ma->pool = pool;
826   ma->match = (void *)match;
827   ma->flags = flags;
828   ma->error = 0;
829   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
830     {
831       ma->match = sat_calloc(1, sizeof(regex_t));
832       ma->error = regcomp((regex_t *)ma->match, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
833       if (ma->error)
834         {
835           sat_free(ma->match);
836           ma->match = (void *)match;
837           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
838         }
839     }
840 }
841
842 void
843 datamatcher_free(Datamatcher *ma)
844 {
845   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->match)
846     {
847       regfree(ma->match);
848       ma->match = sat_free(ma->match);
849     }
850 }
851
852 int
853 datamatcher_match(Datamatcher *ma, Repodata *data, Repokey *key, KeyValue *kv)
854 {
855   switch (key->type)
856     {
857     case REPOKEY_TYPE_ID:
858     case REPOKEY_TYPE_IDARRAY:
859       if (data && data->localpool)
860         kv->str = stringpool_id2str(&data->spool, kv->id);
861       else
862         kv->str = id2str(ma->pool, kv->id);
863       break;
864     case REPOKEY_TYPE_STR:
865       break;
866     case REPOKEY_TYPE_DIRSTRARRAY:
867       if (!(ma->flags & SEARCH_FILES))
868         return 0;
869       /* Put the full filename into kv->str.  */
870       kv->str = repodata_dir2str(data, kv->id, kv->str);
871       /* And to compensate for that put the "empty" directory into
872          kv->id, so that later calls to repodata_dir2str on this data
873          come up with the same filename again.  */
874       kv->id = 0;
875       break;
876     default:
877       return 0;
878     }
879   /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
880      for the others we can't know if a colon separates a kind or not.  */
881   if ((ma->flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
882     {
883       const char *s = strchr(kv->str, ':');
884       if (s)
885         kv->str = s + 1;
886     }
887   switch ((ma->flags & SEARCH_STRINGMASK))
888     {
889     case SEARCH_SUBSTRING:
890       if (ma->flags & SEARCH_NOCASE)
891         {
892           if (!strcasestr(kv->str, (const char *)ma->match))
893             return 0;
894         }
895       else
896         {
897           if (!strstr(kv->str, (const char *)ma->match))
898             return 0;
899         }
900       break;
901     case SEARCH_STRING:
902       if (ma->flags & SEARCH_NOCASE)
903         {
904           if (strcasecmp((const char *)ma->match, kv->str))
905             return 0;
906         }
907       else
908         {
909           if (strcmp((const char *)ma->match, kv->str))
910             return 0;
911         }
912       break;
913     case SEARCH_GLOB:
914       if (fnmatch((const char *)ma->match, kv->str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
915         return 0;
916       break;
917     case SEARCH_REGEX:
918       if (regexec((const regex_t *)ma->match, kv->str, 0, NULL, 0))
919         return 0;
920       break;
921     default:
922       return 0;
923     }
924   return 1;
925 }
926
927 enum {
928   di_bye,
929
930   di_enterrepo,
931   di_entersolvable,
932   di_enterrepodata,
933   di_enterkey,
934
935   di_nextattr,
936   di_nextkey,
937   di_nextrepodata,
938   di_nextsolvable,
939   di_nextrepo,
940
941   di_enterarray,
942   di_nextarrayelement,
943
944   di_entersub,
945   di_leavesub,
946
947   di_nextsolvableattr,
948   di_nextsolvablekey,
949   di_entersolvablekey
950 };
951
952 void
953 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname, const char *match, int flags)
954 {
955   memset(di, 0, sizeof(*di));
956   di->repo = repo;
957   di->keyname = keyname;
958   di->solvid = p;
959   di->pool = repo->pool;
960   if (p)
961     flags |= SEARCH_THISSOLVID;
962   di->flags = flags;
963   if (repo)
964     di->repoid = -1;
965   if (match)
966     datamatcher_init(&di->matcher, di->pool, match, flags);
967   if (p == SOLVID_POS)
968     {
969       di->repo = di->pool->pos.repo;
970       if (!di->repo)
971         {
972           di->state = di_bye;
973           return;
974         }
975       di->data = di->repo->repodata + di->pool->pos.repodataid;
976       di->repoid = -1;
977       di->repodataid = -1;
978     }
979   di->state = di_enterrepo;
980 }
981
982 void
983 dataiterator_free(Dataiterator *di)
984 {
985   if (di->matcher.match)
986     datamatcher_free(&di->matcher);
987 }
988
989 static inline unsigned char *
990 dataiterator_find_keyname(Dataiterator *di, Id keyname)
991 {
992   Id *keyp = di->keyp;
993   Repokey *keys = di->data->keys;
994   unsigned char *dp;
995
996   if (!(di->flags & SEARCH_SUB))
997     {
998       for (keyp = di->keyp; *keyp; keyp++)
999         if (keys[*keyp].name == di->keyname)
1000           break;
1001     }
1002   else
1003     {
1004       for (keyp++; *keyp; keyp++)
1005         if (keys[*keyp].name == di->keyname || 
1006             keys[*keyp].type == REPOKEY_TYPE_FIXARRAY || 
1007             keys[*keyp].type == REPOKEY_TYPE_FLEXARRAY)
1008           break;
1009     }
1010   if (!*keyp)
1011     return 0;
1012   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1013   if (!dp)
1014     return 0;
1015   di->keyp = keyp;
1016   return dp;
1017 }
1018
1019 int
1020 dataiterator_step(Dataiterator *di)
1021 {
1022   Id schema;
1023
1024   for (;;)
1025     {
1026       switch (di->state)
1027         {
1028         case di_enterrepo: di_enterrepo:
1029           if (!(di->flags & SEARCH_THISSOLVID))
1030             di->solvid = di->repo->start;
1031           /* FALLTHROUGH */
1032
1033         case di_entersolvable: di_entersolvable:
1034           if (di->repodataid >= 0)
1035             {
1036               di->repodataid = 0;
1037               if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)))
1038                 {
1039                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1040                   di->data = 0;
1041                   goto di_entersolvablekey;
1042                 }
1043             }
1044           /* FALLTHROUGH */
1045
1046         case di_enterrepodata: di_enterrepodata:
1047           if (di->repodataid >= 0)
1048             di->data = di->repo->repodata + di->repodataid;
1049           if (!maybe_load_repodata(di->data, di->keyname))
1050             goto di_nextrepodata;
1051           di->dp = solvid2data(di->data, di->solvid, &schema);
1052           if (!di->dp)
1053             goto di_nextrepodata;
1054           di->keyp = di->data->schemadata + di->data->schemata[schema];
1055           if (di->keyname)
1056             {
1057               di->dp = dataiterator_find_keyname(di, di->keyname);
1058               if (!di->dp)
1059                 goto di_nextrepodata;
1060             }
1061           /* FALLTHROUGH */
1062
1063         case di_enterkey: di_enterkey:
1064           di->kv.entry = -1;
1065           di->key = di->data->keys + *di->keyp;
1066           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
1067           if (!di->ddp)
1068             goto di_nextkey;
1069           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1070             goto di_enterarray;
1071           /* FALLTHROUGH */
1072
1073         case di_nextattr:
1074           di->kv.entry++;
1075           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1076           if (di->kv.eof)
1077             di->state = di_nextkey;
1078           else
1079             di->state = di_nextattr;
1080           break;
1081
1082         case di_nextkey: di_nextkey:
1083           if (!di->keyname)
1084             {
1085               if (*++di->keyp)
1086                 goto di_enterkey;
1087             }
1088           else if ((di->flags & SEARCH_SUB) != 0)
1089             {
1090               di->dp = dataiterator_find_keyname(di, di->keyname);
1091               if (di->dp)
1092                 goto di_enterkey;
1093             }
1094           if (di->kv.parent)
1095             goto di_leavesub;
1096           /* FALLTHROUGH */
1097
1098         case di_nextrepodata: di_nextrepodata:
1099           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1100               goto di_enterrepodata;
1101           /* FALLTHROUGH */
1102
1103         case di_nextsolvable:
1104           if (!(di->flags & SEARCH_THISSOLVID))
1105             {
1106               if (di->solvid < 0)
1107                 di->solvid = di->repo->start;
1108               else
1109                 di->solvid++;
1110               for (; di->solvid < di->repo->end; di->solvid++)
1111                 {
1112                   if (di->pool->solvables[di->solvid].repo == di->repo)
1113                     goto di_entersolvable;
1114                 }
1115             }
1116           /* FALLTHROUGH */
1117
1118         case di_nextrepo:
1119           if (di->repoid >= 0)
1120             {
1121               di->repoid++;
1122               if (di->repoid < di->pool->nrepos)
1123                 {
1124                   di->repo = di->pool->repos[di->repoid];
1125                   goto di_enterrepo;
1126                 }
1127             }
1128         /* FALLTHROUGH */
1129
1130         case di_bye:
1131           di->state = di_bye;
1132           return 0;
1133
1134         case di_enterarray: di_enterarray:
1135           di->ddp = data_read_id(di->ddp, &di->kv.num);
1136           di->kv.eof = 0;
1137           di->kv.entry = -1;
1138           /* FALLTHROUGH */
1139
1140         case di_nextarrayelement: di_nextarrayelement:
1141           di->kv.entry++;
1142           if (di->kv.entry)
1143             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1144           if (di->kv.entry == di->kv.num)
1145             {
1146               if (di->keyname && di->key->name != di->keyname)
1147                 goto di_nextkey;
1148               di->kv.str = (char *)di->ddp;
1149               di->kv.eof = 1;
1150               di->state = di_nextkey;
1151               break;
1152             }
1153           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1154             di->ddp = data_read_id(di->ddp, &di->kv.id);
1155           di->kv.str = (char *)di->ddp;
1156           if (di->keyname && di->key->name != di->keyname)
1157             goto di_entersub;
1158           if ((di->flags & SEARCH_SUB) != 0)
1159             di->state = di_entersub;
1160           else
1161             di->state = di_nextarrayelement;
1162           break;
1163
1164         case di_entersub: di_entersub:
1165           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1166             goto di_nextarrayelement;   /* sorry, full */
1167           di->parents[di->nparents].kv = di->kv;
1168           di->parents[di->nparents].dp = di->dp;
1169           di->parents[di->nparents].keyp = di->keyp;
1170           di->dp = (unsigned char *)di->kv.str;
1171           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1172           memset(&di->kv, 0, sizeof(di->kv));
1173           di->kv.parent = &di->parents[di->nparents].kv;
1174           di->nparents++;
1175           di->keyp--;
1176           goto di_nextkey;
1177
1178         case di_leavesub: di_leavesub:
1179           di->nparents--;
1180           di->dp = di->parents[di->nparents].dp;
1181           di->kv = di->parents[di->nparents].kv;
1182           di->keyp = di->parents[di->nparents].keyp;
1183           di->key = di->data->keys + *di->keyp;
1184           di->ddp = (unsigned char *)di->kv.str;
1185           goto di_nextarrayelement;
1186
1187         /* special solvable attr handling follows */
1188
1189         case di_nextsolvableattr:
1190           di->kv.id = *di->idp++;
1191           di->kv.entry++;
1192           if (!*di->idp)
1193             {
1194               di->kv.eof = 1;
1195               di->state = di_nextsolvablekey;
1196             }
1197           break;
1198
1199         case di_nextsolvablekey: di_nextsolvablekey:
1200           if (di->keyname || di->key->name == RPM_RPMDBID)
1201             goto di_enterrepodata;
1202           di->key++;
1203           /* FALLTHROUGH */
1204
1205         case di_entersolvablekey: di_entersolvablekey:
1206           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1207           if (!di->idp || !di->idp[0])
1208             goto di_nextsolvablekey;
1209           di->kv.id = di->idp[0];
1210           di->kv.num = di->idp[0];
1211           if (!di->kv.eof && !di->idp[1])
1212             di->kv.eof = 1;
1213           di->kv.entry = 0;
1214           if (di->kv.eof)
1215             di->state = di_nextsolvablekey;
1216           else
1217             di->state = di_nextsolvableattr;
1218           break;
1219         }
1220
1221       if (di->matcher.match)
1222         if (!datamatcher_match(&di->matcher, di->data, di->key, &di->kv))
1223           continue;
1224       /* found something! */
1225       return 1;
1226     }
1227 }
1228
1229 void
1230 dataiterator_entersub(Dataiterator *di)
1231 {
1232   if (di->state == di_nextarrayelement)
1233     di->state = di_entersub;
1234 }
1235
1236 void
1237 dataiterator_setpos(Dataiterator *di)
1238 {
1239   if (di->kv.eof)
1240     {
1241       memset(&di->pool->pos, 0, sizeof(di->pool->pos));
1242       return;
1243     }
1244   di->pool->pos.repo = di->repo;
1245   di->pool->pos.repodataid = di->data - di->repo->repodata;
1246   di->pool->pos.schema = di->kv.id;
1247   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1248 }
1249
1250 void
1251 dataiterator_setpos_parent(Dataiterator *di)
1252 {
1253   if (!di->kv.parent)
1254     {
1255       memset(&di->pool->pos, 0, sizeof(di->pool->pos));
1256       return;
1257     }
1258   di->pool->pos.repo = di->repo;
1259   di->pool->pos.repodataid = di->data - di->repo->repodata;
1260   di->pool->pos.schema = di->kv.parent->id;
1261   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1262 }
1263
1264 void
1265 dataiterator_skip_attribute(Dataiterator *di)
1266 {
1267   if (di->state == di_nextsolvableattr)
1268     di->state = di_nextsolvablekey;
1269   else
1270     di->state = di_nextkey;
1271 }
1272
1273 void
1274 dataiterator_skip_solvable(Dataiterator *di)
1275 {
1276   di->state = di_nextsolvable;
1277 }
1278
1279 void
1280 dataiterator_skip_repo(Dataiterator *di)
1281 {
1282   di->state = di_nextrepo;
1283 }
1284
1285 void
1286 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1287 {
1288   di->repo = s->repo;
1289   di->repoid = -1;
1290   di->solvid = s - di->pool->solvables;
1291   di->state = di_entersolvable;
1292 }
1293
1294 void
1295 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1296 {
1297   di->repo = repo;
1298   di->repoid = -1;
1299   di->state = di_enterrepo;
1300 }
1301
1302 int
1303 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
1304 {
1305   Datamatcher matcher = di->matcher;
1306   matcher.flags = flags;
1307   matcher.match = (void *)vmatch;
1308   matcher.pool = di->pool;
1309   return datamatcher_match(&matcher, di->data, di->key, &di->kv);
1310 }
1311
1312
1313 /************************************************************************
1314  * data modify functions
1315  */
1316
1317 /* extend repodata so that it includes solvables p */
1318 void
1319 repodata_extend(Repodata *data, Id p)
1320 {
1321   if (data->start == data->end)
1322     data->start = data->end = p;
1323   if (p >= data->end)
1324     {
1325       int old = data->end - data->start;
1326       int new = p - data->end + 1;
1327       if (data->attrs)
1328         {
1329           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1330           memset(data->attrs + old, 0, new * sizeof(Id *));
1331         }
1332       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1333       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1334       data->end = p + 1;
1335     }
1336   if (p < data->start)
1337     {
1338       int old = data->end - data->start;
1339       int new = data->start - p;
1340       if (data->attrs)
1341         {
1342           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1343           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1344           memset(data->attrs, 0, new * sizeof(Id *));
1345         }
1346       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1347       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1348       memset(data->incoreoffset, 0, new * sizeof(Id));
1349       data->start = p;
1350     }
1351 }
1352
1353 void
1354 repodata_extend_block(Repodata *data, Id start, Id num)
1355 {
1356   if (!num)
1357     return;
1358   if (!data->incoreoffset)
1359     {
1360       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1361       data->start = start;
1362       data->end = start + num;
1363       return;
1364     }
1365   repodata_extend(data, start);
1366   if (num > 1)
1367     repodata_extend(data, start + num - 1);
1368 }
1369
1370 /**********************************************************************/
1371
1372 #define REPODATA_ATTRS_BLOCK 63
1373 #define REPODATA_ATTRDATA_BLOCK 1023
1374 #define REPODATA_ATTRIDDATA_BLOCK 63
1375
1376
1377 Id
1378 repodata_new_handle(Repodata *data)
1379 {
1380   if (!data->nxattrs)
1381     {
1382       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1383       data->nxattrs = 2;
1384     }
1385   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1386   data->xattrs[data->nxattrs] = 0;
1387   return -(data->nxattrs++);
1388 }
1389
1390 static inline Id **
1391 repodata_get_attrp(Repodata *data, Id handle)
1392 {
1393   if (handle == SOLVID_META)
1394     {
1395       if (!data->xattrs)
1396         {
1397           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1398           data->nxattrs = 2;
1399         }
1400     }
1401   if (handle < 0)
1402     return data->xattrs - handle;
1403   if (handle < data->start || handle >= data->end)
1404     repodata_extend(data, handle);
1405   if (!data->attrs)
1406     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1407   return data->attrs + (handle - data->start);
1408 }
1409
1410 static void
1411 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1412 {
1413   Id *pp;
1414   Id *ap, **app;
1415   int i;
1416
1417   app = repodata_get_attrp(data, handle);
1418   ap = *app;
1419   i = 0;
1420   if (ap)
1421     {
1422       for (pp = ap; *pp; pp += 2)
1423         /* Determine equality based on the name only, allows us to change
1424            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1425         if (data->keys[*pp].name == data->keys[keyid].name)
1426           break;
1427       if (*pp)
1428         {
1429           if (overwrite)
1430             {
1431               pp[0] = keyid;
1432               pp[1] = val;
1433             }
1434           return;
1435         }
1436       i = pp - ap;
1437     }
1438   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1439   *app = ap;
1440   pp = ap + i;
1441   *pp++ = keyid;
1442   *pp++ = val;
1443   *pp = 0;
1444 }
1445
1446
1447 void
1448 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1449 {
1450   Id keyid;
1451
1452   keyid = repodata_key2id(data, key, 1);
1453   repodata_insert_keyid(data, solvid, keyid, val, 1);
1454 }
1455
1456 void
1457 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1458 {
1459   Repokey key;
1460   key.name = keyname;
1461   key.type = REPOKEY_TYPE_ID;
1462   key.size = 0;
1463   key.storage = KEY_STORAGE_INCORE;
1464   repodata_set(data, solvid, &key, id);
1465 }
1466
1467 void
1468 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1469 {
1470   Repokey key;
1471   key.name = keyname;
1472   key.type = REPOKEY_TYPE_NUM;
1473   key.size = 0;
1474   key.storage = KEY_STORAGE_INCORE;
1475   repodata_set(data, solvid, &key, (Id)num);
1476 }
1477
1478 void
1479 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
1480 {
1481   Repokey key;
1482   Id id;
1483   if (data->localpool)
1484     id = stringpool_str2id(&data->spool, str, 1);
1485   else
1486     id = str2id(data->repo->pool, str, 1);
1487   key.name = keyname;
1488   key.type = REPOKEY_TYPE_ID;
1489   key.size = 0;
1490   key.storage = KEY_STORAGE_INCORE;
1491   repodata_set(data, solvid, &key, id);
1492 }
1493
1494 void
1495 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
1496 {
1497   Repokey key;
1498   key.name = keyname;
1499   key.type = REPOKEY_TYPE_CONSTANT;
1500   key.size = constant;
1501   key.storage = KEY_STORAGE_INCORE;
1502   repodata_set(data, solvid, &key, 0);
1503 }
1504
1505 void
1506 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
1507 {
1508   Repokey key;
1509   key.name = keyname;
1510   key.type = REPOKEY_TYPE_CONSTANTID;
1511   key.size = id;
1512   key.storage = KEY_STORAGE_INCORE;
1513   repodata_set(data, solvid, &key, 0);
1514 }
1515
1516 void
1517 repodata_set_void(Repodata *data, Id solvid, Id keyname)
1518 {
1519   Repokey key;
1520   key.name = keyname;
1521   key.type = REPOKEY_TYPE_VOID;
1522   key.size = 0;
1523   key.storage = KEY_STORAGE_INCORE;
1524   repodata_set(data, solvid, &key, 0);
1525 }
1526
1527 void
1528 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
1529 {
1530   Repokey key;
1531   int l;
1532
1533   l = strlen(str) + 1;
1534   key.name = keyname;
1535   key.type = REPOKEY_TYPE_STR;
1536   key.size = 0;
1537   key.storage = KEY_STORAGE_INCORE;
1538   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1539   memcpy(data->attrdata + data->attrdatalen, str, l);
1540   repodata_set(data, solvid, &key, data->attrdatalen);
1541   data->attrdatalen += l;
1542 }
1543
1544 static void
1545 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1546 {
1547   int oldsize;
1548   Id *ida, *pp, **ppp;
1549
1550   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1551     {
1552       /* great! just append the new data */
1553       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1554       data->attriddatalen--;    /* overwrite terminating 0  */
1555       data->lastdatalen += entrysize;
1556       return;
1557     }
1558   ppp = repodata_get_attrp(data, handle);
1559   pp = *ppp;
1560   if (pp)
1561     for (; *pp; pp += 2)
1562       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1563         break;
1564   if (!pp || !*pp)
1565     {
1566       /* not found. allocate new key */
1567       Repokey key;
1568       key.name = keyname;
1569       key.type = keytype;
1570       key.size = 0;
1571       key.storage = KEY_STORAGE_INCORE;
1572       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1573       repodata_set(data, handle, &key, data->attriddatalen);
1574       data->lasthandle = 0;     /* next time... */
1575       return;
1576     }
1577   oldsize = 0;
1578   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1579     oldsize += entrysize;
1580   if (ida + 1 == data->attriddata + data->attriddatalen)
1581     {
1582       /* this was the last entry, just append it */
1583       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1584       data->attriddatalen--;    /* overwrite terminating 0  */
1585     }
1586   else
1587     {
1588       /* too bad. move to back. */
1589       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1590       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1591       pp[1] = data->attriddatalen;
1592       data->attriddatalen += oldsize;
1593     }
1594   data->lasthandle = handle;
1595   data->lastkey = *pp;
1596   data->lastdatalen = data->attriddatalen + entrysize + 1;
1597 }
1598
1599 static inline int
1600 checksumtype2len(Id type)
1601 {
1602   switch (type)
1603     {
1604     case REPOKEY_TYPE_MD5:
1605       return SIZEOF_MD5;
1606     case REPOKEY_TYPE_SHA1:
1607       return SIZEOF_SHA1;
1608     case REPOKEY_TYPE_SHA256:
1609       return SIZEOF_SHA256;
1610     default:
1611       return 0;
1612     }
1613 }
1614
1615 void
1616 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1617                       const unsigned char *str)
1618 {
1619   Repokey key;
1620   int l = checksumtype2len(type);
1621
1622   if (!l)
1623     return;
1624   key.name = keyname;
1625   key.type = type;
1626   key.size = 0;
1627   key.storage = KEY_STORAGE_INCORE;
1628   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1629   memcpy(data->attrdata + data->attrdatalen, str, l);
1630   repodata_set(data, solvid, &key, data->attrdatalen);
1631   data->attrdatalen += l;
1632 }
1633
1634 static int
1635 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1636 {
1637   int i;
1638   for (i = 0; i < buflen; i++)
1639     {
1640 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1641                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1642                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1643                 : -1)
1644       int v = c2h(*str);
1645       str++;
1646       if (v < 0)
1647         return 0;
1648       buf[i] = v;
1649       v = c2h(*str);
1650       str++;
1651       if (v < 0)
1652         return 0;
1653       buf[i] = (buf[i] << 4) | v;
1654 #undef c2h
1655     }
1656   return buflen;
1657 }
1658
1659 void
1660 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1661                       const char *str)
1662 {
1663   unsigned char buf[64];
1664   int l = checksumtype2len(type);
1665
1666   if (!l)
1667     return;
1668   if (hexstr2bytes(buf, str, l) != l)
1669     {
1670       fprintf(stderr, "Invalid hex character in '%s'\n", str);
1671       return;
1672     }
1673   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
1674 }
1675
1676 const char *
1677 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1678 {
1679   int i, l;
1680   char *str, *s;
1681
1682   l = checksumtype2len(type);
1683   if (!l)
1684     return "";
1685   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1686   for (i = 0; i < l; i++)
1687     {
1688       unsigned char v = buf[i];
1689       unsigned char w = v >> 4;
1690       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1691       w = v & 15;
1692       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1693     }
1694   *s = 0;
1695   return str;
1696 }
1697
1698 Id
1699 repodata_globalize_id(Repodata *data, Id id)
1700 {
1701   if (!data || !data->localpool)
1702     return id;
1703   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1704 }
1705
1706 void
1707 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
1708 {
1709   assert(dir);
1710 #if 0
1711 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
1712 #endif
1713   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1714   data->attriddata[data->attriddatalen++] = dir;
1715   data->attriddata[data->attriddatalen++] = num;
1716   data->attriddata[data->attriddatalen++] = num2;
1717   data->attriddata[data->attriddatalen++] = 0;
1718 }
1719
1720 void
1721 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
1722 {
1723   Id stroff;
1724   int l;
1725
1726   assert(dir);
1727   l = strlen(str) + 1;
1728   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1729   memcpy(data->attrdata + data->attrdatalen, str, l);
1730   stroff = data->attrdatalen;
1731   data->attrdatalen += l;
1732
1733 #if 0
1734 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
1735 #endif
1736   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1737   data->attriddata[data->attriddatalen++] = dir;
1738   data->attriddata[data->attriddatalen++] = stroff;
1739   data->attriddata[data->attriddatalen++] = 0;
1740 }
1741
1742 void
1743 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
1744 {
1745 #if 0
1746 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
1747 #endif
1748   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
1749   data->attriddata[data->attriddatalen++] = id;
1750   data->attriddata[data->attriddatalen++] = 0;
1751 }
1752
1753 void
1754 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
1755                            const char *str)
1756 {
1757   Id id;
1758   if (data->localpool)
1759     id = stringpool_str2id(&data->spool, str, 1);
1760   else
1761     id = str2id(data->repo->pool, str, 1);
1762   repodata_add_idarray(data, solvid, keyname, id);
1763 }
1764
1765 void
1766 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1767 {
1768   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
1769   data->attriddata[data->attriddatalen++] = ghandle;
1770   data->attriddata[data->attriddatalen++] = 0;
1771 }
1772
1773 void
1774 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1775 {
1776   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
1777   data->attriddata[data->attriddatalen++] = ghandle;
1778   data->attriddata[data->attriddatalen++] = 0;
1779 }
1780
1781 void
1782 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1783 {
1784   Id *keyp;
1785   if (dest == src || !(keyp = data->attrs[src]))
1786     return;
1787   for (; *keyp; keyp += 2)
1788     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1789 }
1790
1791
1792
1793
1794 /**********************************************************************/
1795
1796 /* unify with repo_write! */
1797
1798 #define EXTDATA_BLOCK 1023
1799
1800 struct extdata {
1801   unsigned char *buf;
1802   int len;
1803 };
1804
1805 static void
1806 data_addid(struct extdata *xd, Id x)
1807 {
1808   unsigned char *dp;
1809   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1810   dp = xd->buf + xd->len;
1811
1812   if (x >= (1 << 14))
1813     {
1814       if (x >= (1 << 28))
1815         *dp++ = (x >> 28) | 128;
1816       if (x >= (1 << 21))
1817         *dp++ = (x >> 21) | 128;
1818       *dp++ = (x >> 14) | 128;
1819     }
1820   if (x >= (1 << 7))
1821     *dp++ = (x >> 7) | 128;
1822   *dp++ = x & 127;
1823   xd->len = dp - xd->buf;
1824 }
1825
1826 static void
1827 data_addideof(struct extdata *xd, Id x, int eof)
1828 {
1829   if (x >= 64)
1830     x = (x & 63) | ((x & ~63) << 1);
1831   data_addid(xd, (eof ? x: x | 64));
1832 }
1833
1834 static void
1835 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1836 {
1837   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1838   memcpy(xd->buf + xd->len, blob, len);
1839   xd->len += len;
1840 }
1841
1842 /*********************************/
1843
1844 static void
1845 repodata_serialize_key(Repodata *data, struct extdata *newincore,
1846                        struct extdata *newvincore,
1847                        Id *schema,
1848                        Repokey *key, Id val)
1849 {
1850   /* Otherwise we have a new value.  Parse it into the internal
1851      form.  */
1852   Id *ida;
1853   struct extdata *xd;
1854   unsigned int oldvincorelen = 0;
1855   Id schemaid, *sp;
1856
1857   xd = newincore;
1858   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1859     {
1860       xd = newvincore;
1861       oldvincorelen = xd->len;
1862     }
1863   switch (key->type)
1864     {
1865     case REPOKEY_TYPE_VOID:
1866     case REPOKEY_TYPE_CONSTANT:
1867     case REPOKEY_TYPE_CONSTANTID:
1868       break;
1869     case REPOKEY_TYPE_STR:
1870       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
1871       break;
1872     case REPOKEY_TYPE_MD5:
1873       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
1874       break;
1875     case REPOKEY_TYPE_SHA1:
1876       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
1877       break;
1878     case REPOKEY_TYPE_SHA256:
1879       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
1880       break;
1881     case REPOKEY_TYPE_ID:
1882     case REPOKEY_TYPE_NUM:
1883     case REPOKEY_TYPE_DIR:
1884       data_addid(xd, val);
1885       break;
1886     case REPOKEY_TYPE_IDARRAY:
1887       for (ida = data->attriddata + val; *ida; ida++)
1888         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1889       break;
1890     case REPOKEY_TYPE_DIRNUMNUMARRAY:
1891       for (ida = data->attriddata + val; *ida; ida += 3)
1892         {
1893           data_addid(xd, ida[0]);
1894           data_addid(xd, ida[1]);
1895           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1896         }
1897       break;
1898     case REPOKEY_TYPE_DIRSTRARRAY:
1899       for (ida = data->attriddata + val; *ida; ida += 2)
1900         {
1901           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1902           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1903         }
1904       break;
1905     case REPOKEY_TYPE_FIXARRAY:
1906       {
1907         int num = 0;
1908         schemaid = 0;
1909         for (ida = data->attriddata + val; *ida; ida++)
1910           {
1911 #if 0
1912             fprintf(stderr, "serialize struct %d\n", *ida);
1913 #endif
1914             sp = schema;
1915             Id *kp = data->xattrs[-*ida];
1916             if (!kp)
1917               continue;
1918             num++;
1919             for (;*kp; kp += 2)
1920               {
1921 #if 0
1922                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
1923 #endif
1924                 *sp++ = *kp;
1925               }
1926             *sp = 0;
1927             if (!schemaid)
1928               schemaid = repodata_schema2id(data, schema, 1);
1929             else if (schemaid != repodata_schema2id(data, schema, 0))
1930               {
1931                 fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
1932                 exit(1);
1933               }
1934 #if 0
1935             fprintf(stderr, "  schema %d\n", schemaid);
1936 #endif
1937           }
1938         if (!num)
1939           break;
1940         data_addid(xd, num);
1941         data_addid(xd, schemaid);
1942         for (ida = data->attriddata + val; *ida; ida++)
1943           {
1944             Id *kp = data->xattrs[-*ida];
1945             if (!kp)
1946               continue;
1947             for (;*kp; kp += 2)
1948               {
1949                 repodata_serialize_key(data, newincore, newvincore,
1950                                        schema, data->keys + *kp, kp[1]);
1951               }
1952           }
1953         break;
1954       }
1955     case REPOKEY_TYPE_FLEXARRAY:
1956       {
1957         int num = 0;
1958         for (ida = data->attriddata + val; *ida; ida++)
1959           num++;
1960         data_addid(xd, num);
1961         for (ida = data->attriddata + val; *ida; ida++)
1962           {
1963             Id *kp = data->xattrs[-*ida];
1964             if (!kp)
1965               {
1966                 data_addid(xd, 0);      /* XXX */
1967                 continue;
1968               }
1969             sp = schema;
1970             for (;*kp; kp += 2)
1971               *sp++ = *kp;
1972             *sp = 0;
1973             schemaid = repodata_schema2id(data, schema, 1);
1974             data_addid(xd, schemaid);
1975             kp = data->xattrs[-*ida];
1976             for (;*kp; kp += 2)
1977               {
1978                 repodata_serialize_key(data, newincore, newvincore,
1979                                        schema, data->keys + *kp, kp[1]);
1980               }
1981           }
1982         break;
1983       }
1984     default:
1985       fprintf(stderr, "don't know how to handle type %d\n", key->type);
1986       exit(1);
1987     }
1988   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1989     {
1990       /* put offset/len in incore */
1991       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
1992       oldvincorelen = xd->len - oldvincorelen;
1993       data_addid(newincore, oldvincorelen);
1994     }
1995 }
1996
1997 void
1998 repodata_internalize(Repodata *data)
1999 {
2000   Repokey *key, solvkey;
2001   Id entry, nentry;
2002   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2003   unsigned char *dp, *ndp;
2004   int newschema, oldcount;
2005   struct extdata newincore;
2006   struct extdata newvincore;
2007   Id solvkeyid;
2008
2009   if (!data->attrs && !data->xattrs)
2010     return;
2011
2012   newvincore.buf = data->vincore;
2013   newvincore.len = data->vincorelen;
2014
2015   /* find the solvables key, create if needed */
2016   memset(&solvkey, 0, sizeof(solvkey));
2017   solvkey.name = REPOSITORY_SOLVABLES;
2018   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2019   solvkey.size = 0;
2020   solvkey.storage = KEY_STORAGE_INCORE;
2021   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2022
2023   schema = sat_malloc2(data->nkeys, sizeof(Id));
2024   seen = sat_malloc2(data->nkeys, sizeof(Id));
2025
2026   /* Merge the data already existing (in data->schemata, ->incoredata and
2027      friends) with the new attributes in data->attrs[].  */
2028   nentry = data->end - data->start;
2029   memset(&newincore, 0, sizeof(newincore));
2030   data_addid(&newincore, 0);    /* start data at offset 1 */
2031
2032   data->mainschema = 0;
2033   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2034
2035   /* join entry data */
2036   /* we start with the meta data, entry -1 */
2037   for (entry = -1; entry < nentry; entry++)
2038     {
2039       memset(seen, 0, data->nkeys * sizeof(Id));
2040       oldschema = 0;
2041       dp = data->incoredata;
2042       if (dp)
2043         {
2044           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2045           dp = data_read_id(dp, &oldschema);
2046         }
2047 #if 0
2048 fprintf(stderr, "oldschema %d\n", oldschema);
2049 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2050 fprintf(stderr, "schemadata %p\n", data->schemadata);
2051 #endif
2052       /* seen: -1: old data  0: skipped  >0: id + 1 */
2053       newschema = 0;
2054       oldcount = 0;
2055       sp = schema;
2056       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2057         {
2058           if (seen[*keyp])
2059             {
2060               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
2061               exit(1);
2062             }
2063           seen[*keyp] = -1;
2064           *sp++ = *keyp;
2065           oldcount++;
2066         }
2067       if (entry >= 0)
2068         keyp = data->attrs ? data->attrs[entry] : 0;
2069       else
2070         {
2071           /* strip solvables key */
2072           *sp = 0;
2073           for (sp = keyp = schema; *sp; sp++)
2074             if (*sp != solvkeyid)
2075               *keyp++ = *sp;
2076             else
2077               oldcount--;
2078           sp = keyp;
2079           seen[solvkeyid] = 0;
2080           keyp = data->xattrs ? data->xattrs[1] : 0;
2081         }
2082       if (keyp)
2083         for (; *keyp; keyp += 2)
2084           {
2085             if (!seen[*keyp])
2086               {
2087                 newschema = 1;
2088                 *sp++ = *keyp;
2089               }
2090             seen[*keyp] = keyp[1] + 1;
2091           }
2092       if (entry < 0 && data->end != data->start)
2093         {
2094           *sp++ = solvkeyid;
2095           newschema = 1;
2096         }
2097       *sp = 0;
2098       if (newschema)
2099         /* Ideally we'd like to sort the new schema here, to ensure
2100            schema equality independend of the ordering.  We can't do that
2101            yet.  For once see below (old ids need to come before new ids).
2102            An additional difficulty is that we also need to move
2103            the values with the keys.  */
2104         schemaid = repodata_schema2id(data, schema, 1);
2105       else
2106         schemaid = oldschema;
2107
2108
2109       /* Now create data blob.  We walk through the (possibly new) schema
2110          and either copy over old data, or insert the new.  */
2111       /* XXX Here we rely on the fact that the (new) schema has the form
2112          o1 o2 o3 o4 ... | n1 n2 n3 ...
2113          (oX being the old keyids (possibly overwritten), and nX being
2114           the new keyids).  This rules out sorting the keyids in order
2115          to ensure a small schema count.  */
2116       if (entry >= 0)
2117         data->incoreoffset[entry] = newincore.len;
2118       data_addid(&newincore, schemaid);
2119       if (entry == -1)
2120         {
2121           data->mainschema = schemaid;
2122           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2123         }
2124       keypstart = data->schemadata + data->schemata[schemaid];
2125       for (keyp = keypstart; *keyp; keyp++)
2126         {
2127           if (entry == -1)
2128             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2129           if (*keyp == solvkeyid)
2130             {
2131               /* add flexarray entry count */
2132               data_addid(&newincore, data->end - data->start);
2133               break;
2134             }
2135           key = data->keys + *keyp;
2136 #if 0
2137           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2138 #endif
2139           ndp = dp;
2140           if (oldcount)
2141             {
2142               /* Skip the data associated with this old key.  */
2143               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2144                 {
2145                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2146                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2147                 }
2148               else if (key->storage == KEY_STORAGE_INCORE)
2149                 ndp = data_skip_key(data, dp, key);
2150               oldcount--;
2151             }
2152           if (seen[*keyp] == -1)
2153             {
2154               /* If this key was an old one _and_ was not overwritten with
2155                  a different value copy over the old value (we skipped it
2156                  above).  */
2157               if (dp != ndp)
2158                 data_addblob(&newincore, dp, ndp - dp);
2159               seen[*keyp] = 0;
2160             }
2161           else if (seen[*keyp])
2162             {
2163               /* Otherwise we have a new value.  Parse it into the internal
2164                  form.  */
2165               repodata_serialize_key(data, &newincore, &newvincore,
2166                                      schema, key, seen[*keyp] - 1);
2167             }
2168           dp = ndp;
2169         }
2170       if (entry >= 0 && data->attrs && data->attrs[entry])
2171         data->attrs[entry] = sat_free(data->attrs[entry]);
2172     }
2173   /* free all xattrs */
2174   for (entry = 0; entry < data->nxattrs; entry++)
2175     if (data->xattrs[entry])
2176       sat_free(data->xattrs[entry]);
2177   data->xattrs = sat_free(data->xattrs);
2178   data->nxattrs = 0;
2179
2180   data->lasthandle = 0;
2181   data->lastkey = 0;
2182   data->lastdatalen = 0;
2183   sat_free(schema);
2184   sat_free(seen);
2185   repodata_free_schemahash(data);
2186
2187   sat_free(data->incoredata);
2188   data->incoredata = newincore.buf;
2189   data->incoredatalen = newincore.len;
2190   data->incoredatafree = 0;
2191
2192   sat_free(data->vincore);
2193   data->vincore = newvincore.buf;
2194   data->vincorelen = newvincore.len;
2195
2196   data->attrs = sat_free(data->attrs);
2197   data->attrdata = sat_free(data->attrdata);
2198   data->attriddata = sat_free(data->attriddata);
2199   data->attrdatalen = 0;
2200   data->attriddatalen = 0;
2201 }
2202
2203 void
2204 repodata_disable_paging(Repodata *data)
2205 {
2206   if (maybe_load_repodata(data, 0))
2207     repopagestore_disable_paging(&data->store);
2208 }
2209
2210 /*
2211 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2212 */