- revert strange change from Klaus
[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
615 int
616 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
617 {
618   switch (key->type)
619     {
620     case REPOKEY_TYPE_ID:
621     case REPOKEY_TYPE_CONSTANTID:
622     case REPOKEY_TYPE_IDARRAY:
623       if (data && data->localpool)
624         kv->str = stringpool_id2str(&data->spool, kv->id);
625       else
626         kv->str = id2str(pool, kv->id);
627       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
628         {
629           const char *s = strchr(kv->str, ':');
630           if (s)
631             kv->str = s + 1;
632         }
633       return 1;
634     case REPOKEY_TYPE_STR:
635       return 1;
636     case REPOKEY_TYPE_DIRSTRARRAY:
637       if (!(flags & SEARCH_FILES))
638         return 1;       /* match just the basename */
639       /* Put the full filename into kv->str.  */
640       kv->str = repodata_dir2str(data, kv->id, kv->str);
641       /* And to compensate for that put the "empty" directory into
642          kv->id, so that later calls to repodata_dir2str on this data
643          come up with the same filename again.  */
644       kv->id = 0;
645       return 1;
646     default:
647       return 0;
648     }
649 }
650
651
652 struct subschema_data {
653   Solvable *s;
654   void *cbdata;
655   KeyValue *parent;
656 };
657
658 /* search a specific repodata */
659 void
660 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
661 {
662   Id schema;
663   Repokey *key;
664   Id keyid, *kp, *keyp;
665   unsigned char *dp, *ddp;
666   int onekey = 0;
667   int stop;
668   KeyValue kv;
669   Solvable *s;
670
671   if (!maybe_load_repodata(data, keyname))
672     return;
673   if (solvid == SOLVID_SUBSCHEMA)
674     {
675       struct subschema_data *subd = cbdata;
676       cbdata = subd->cbdata;
677       s = subd->s;
678       schema = subd->parent->id;
679       dp = (unsigned char *)subd->parent->str;
680       kv.parent = subd->parent;
681     }
682   else
683     {
684       schema = 0;
685       dp = solvid2data(data, solvid, &schema);
686       if (!dp)
687         return;
688       s = data->repo->pool->solvables + solvid;
689       kv.parent = 0;
690     }
691   keyp = data->schemadata + data->schemata[schema];
692   if (keyname)
693     {
694       /* search for a specific key */
695       for (kp = keyp; *kp; kp++)
696         if (data->keys[*kp].name == keyname)
697           break;
698       if (!*kp)
699         return;
700       dp = forward_to_key(data, *kp, keyp, dp);
701       if (!dp)
702         return;
703       keyp = kp;
704       onekey = 1;
705     }
706   while ((keyid = *keyp++) != 0)
707     {
708       stop = 0;
709       key = data->keys + keyid;
710       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
711
712       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
713         {
714           struct subschema_data subd;
715           int nentries;
716           Id schema = 0;
717
718           subd.cbdata = cbdata;
719           subd.s = s;
720           subd.parent = &kv;
721           ddp = data_read_id(ddp, &nentries);
722           kv.num = nentries;
723           kv.entry = 0;
724           kv.eof = 0;
725           while (ddp && nentries > 0)
726             {
727               if (!--nentries)
728                 kv.eof = 1;
729               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
730                 ddp = data_read_id(ddp, &schema);
731               kv.id = schema;
732               kv.str = (char *)ddp;
733               stop = callback(cbdata, s, data, key, &kv);
734               if (stop > SEARCH_NEXT_KEY)
735                 return;
736               if (stop && stop != SEARCH_ENTERSUB)
737                 break;
738               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
739                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
740               ddp = data_skip_schema(data, ddp, schema);
741               kv.entry++;
742             }
743           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
744             {
745               /* sentinel */
746               kv.eof = 2;
747               kv.str = (char *)ddp;
748               stop = callback(cbdata, s, data, key, &kv);
749               if (stop > SEARCH_NEXT_KEY)
750                 return;
751             }
752           if (onekey)
753             return;
754           continue;
755         }
756       kv.entry = 0;
757       do
758         {
759           ddp = data_fetch(ddp, &kv, key);
760           if (!ddp)
761             break;
762           stop = callback(cbdata, s, data, key, &kv);
763           kv.entry++;
764         }
765       while (!kv.eof && !stop);
766       if (onekey || stop > SEARCH_NEXT_KEY)
767         return;
768     }
769 }
770
771 void
772 repodata_setpos_kv(Repodata *data, KeyValue *kv)
773 {
774   Pool *pool = data->repo->pool;
775   if (!kv)
776     pool_clear_pos(pool);
777   else
778     {
779       pool->pos.repo = data->repo;
780       pool->pos.repodataid = data - data->repo->repodata;
781       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
782       pool->pos.schema = kv->id;
783     }
784 }
785
786 /************************************************************************
787  * data iterator functions
788  */
789
790 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
791   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
792   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
793   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
794   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
795   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
796   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
797   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
798   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
799   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
800   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
801   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
802   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
803   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
804 };
805
806 static inline Id *
807 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
808 {
809   kv->id = keyname;
810   switch (keyname)
811     {
812     case SOLVABLE_NAME:
813       kv->eof = 1;
814       return &s->name;
815     case SOLVABLE_ARCH:
816       kv->eof = 1;
817       return &s->arch;
818     case SOLVABLE_EVR:
819       kv->eof = 1;
820       return &s->evr;
821     case SOLVABLE_VENDOR:
822       kv->eof = 1;
823       return &s->vendor;
824     case SOLVABLE_PROVIDES:
825       kv->eof = 0;
826       return s->provides ? s->repo->idarraydata + s->provides : 0;
827     case SOLVABLE_OBSOLETES:
828       kv->eof = 0;
829       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
830     case SOLVABLE_CONFLICTS:
831       kv->eof = 0;
832       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
833     case SOLVABLE_REQUIRES:
834       kv->eof = 0;
835       return s->requires ? s->repo->idarraydata + s->requires : 0;
836     case SOLVABLE_RECOMMENDS:
837       kv->eof = 0;
838       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
839     case SOLVABLE_SUPPLEMENTS:
840       kv->eof = 0;
841       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
842     case SOLVABLE_SUGGESTS:
843       kv->eof = 0;
844       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
845     case SOLVABLE_ENHANCES:
846       kv->eof = 0;
847       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
848     case RPM_RPMDBID:
849       kv->eof = 1;
850       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
851     default:
852       return 0;
853     }
854 }
855
856 int
857 datamatcher_init(Datamatcher *ma, const char *match, int flags)
858 {
859   ma->match = (void *)match;
860   ma->flags = flags;
861   ma->error = 0;
862   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
863     {
864       ma->match = sat_calloc(1, sizeof(regex_t));
865       ma->error = regcomp((regex_t *)ma->match, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
866       if (ma->error)
867         {
868           sat_free(ma->match);
869           ma->match = (void *)match;
870           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
871         }
872     }
873   return ma->error;
874 }
875
876 void
877 datamatcher_free(Datamatcher *ma)
878 {
879   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->match)
880     {
881       regfree(ma->match);
882       ma->match = sat_free(ma->match);
883     }
884 }
885
886 int
887 datamatcher_match(Datamatcher *ma, const char *str)
888 {
889   switch ((ma->flags & SEARCH_STRINGMASK))
890     {
891     case SEARCH_SUBSTRING:
892       if (ma->flags & SEARCH_NOCASE)
893         {
894           if (!strcasestr(str, (const char *)ma->match))
895             return 0;
896         }
897       else
898         {
899           if (!strstr(str, (const char *)ma->match))
900             return 0;
901         }
902       break;
903     case SEARCH_STRING:
904       if (ma->flags & SEARCH_NOCASE)
905         {
906           if (strcasecmp((const char *)ma->match, str))
907             return 0;
908         }
909       else
910         {
911           if (strcmp((const char *)ma->match, str))
912             return 0;
913         }
914       break;
915     case SEARCH_GLOB:
916       if (fnmatch((const char *)ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
917         return 0;
918       break;
919     case SEARCH_REGEX:
920       if (regexec((const regex_t *)ma->match, str, 0, NULL, 0))
921         return 0;
922       break;
923     default:
924       return 0;
925     }
926   return 1;
927 }
928
929 enum {
930   di_bye,
931
932   di_enterrepo,
933   di_entersolvable,
934   di_enterrepodata,
935   di_enterschema,
936   di_enterkey,
937
938   di_nextattr,
939   di_nextkey,
940   di_nextrepodata,
941   di_nextsolvable,
942   di_nextrepo,
943
944   di_enterarray,
945   di_nextarrayelement,
946
947   di_entersub,
948   di_leavesub,
949
950   di_nextsolvableattr,
951   di_nextsolvablekey,
952   di_entersolvablekey
953 };
954
955 int
956 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
957 {
958   memset(di, 0, sizeof(*di));
959   di->pool = pool;
960   if (match)
961     {
962       int error;
963       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
964         {
965           di->state = di_bye;
966           return error;
967         }
968     }
969   di->repo = repo;
970   di->keyname = keyname;
971   di->keynames[0] = keyname;
972   di->flags = flags & ~SEARCH_THISSOLVID;
973   if (!pool->nrepos)
974     {
975       di->state = di_bye;
976       return 0;
977     }
978   if (repo)
979     di->repoid = -1;
980   if (p)
981     dataiterator_jump_to_solvid(di, p);
982   else
983     {
984       di->repo = pool->repos[0];
985       di->state = di_enterrepo;
986     }
987   return 0;
988 }
989
990 void
991 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
992 {
993   int i;
994
995   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
996     {
997       di->state = di_bye;       /* sorry */
998       return;
999     }
1000   for (i = di->nkeynames + 1; i > 0; i--)
1001     di->keynames[i] = di->keynames[i - 1];
1002   di->keynames[0] = di->keyname = keyname;
1003   di->nkeynames++;
1004 }
1005
1006 void
1007 dataiterator_free(Dataiterator *di)
1008 {
1009   if (di->matcher.match)
1010     datamatcher_free(&di->matcher);
1011 }
1012
1013 static inline unsigned char *
1014 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1015 {
1016   Id *keyp = di->keyp;
1017   Repokey *keys = di->data->keys;
1018   unsigned char *dp;
1019
1020   for (keyp = di->keyp; *keyp; keyp++)
1021     if (keys[*keyp].name == keyname)
1022       break;
1023   if (!*keyp)
1024     return 0;
1025   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1026   if (!dp)
1027     return 0;
1028   di->keyp = keyp;
1029   return dp;
1030 }
1031
1032 int
1033 dataiterator_step(Dataiterator *di)
1034 {
1035   Id schema;
1036
1037   for (;;)
1038     {
1039       switch (di->state)
1040         {
1041         case di_enterrepo: di_enterrepo:
1042           if (!(di->flags & SEARCH_THISSOLVID))
1043             {
1044               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1045               goto di_nextsolvable;
1046             }
1047           /* FALLTHROUGH */
1048
1049         case di_entersolvable: di_entersolvable:
1050           if (di->repodataid >= 0)
1051             {
1052               di->repodataid = 0;       /* reset repodata iterator */
1053               if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)))
1054                 {
1055                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1056                   di->data = 0;
1057                   goto di_entersolvablekey;
1058                 }
1059             }
1060           /* FALLTHROUGH */
1061
1062         case di_enterrepodata: di_enterrepodata:
1063           if (di->repodataid >= 0)
1064             {
1065               if (di->repodataid >= di->repo->nrepodata)
1066                 goto di_nextsolvable;
1067               di->data = di->repo->repodata + di->repodataid;
1068             }
1069           if (!maybe_load_repodata(di->data, di->keyname))
1070             goto di_nextrepodata;
1071           di->dp = solvid2data(di->data, di->solvid, &schema);
1072           if (!di->dp)
1073             goto di_nextrepodata;
1074           /* reset key iterator */
1075           di->keyp = di->data->schemadata + di->data->schemata[schema];
1076           /* FALLTHROUGH */
1077
1078         case di_enterschema: di_enterschema:
1079           if (di->keyname)
1080             di->dp = dataiterator_find_keyname(di, di->keyname);
1081           if (!di->dp || !*di->keyp)
1082             {
1083               if (di->kv.parent)
1084                 goto di_leavesub;
1085               goto di_nextrepodata;
1086             }
1087           /* FALLTHROUGH */
1088
1089         case di_enterkey: di_enterkey:
1090           di->kv.entry = -1;
1091           di->key = di->data->keys + *di->keyp;
1092           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
1093           if (!di->ddp)
1094             goto di_nextkey;
1095           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1096             goto di_enterarray;
1097           if (di->nparents < di->nkeynames)
1098             goto di_nextkey;
1099           /* FALLTHROUGH */
1100
1101         case di_nextattr:
1102           di->kv.entry++;
1103           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1104           if (di->kv.eof)
1105             di->state = di_nextkey;
1106           else
1107             di->state = di_nextattr;
1108           break;
1109
1110         case di_nextkey: di_nextkey:
1111           if (!di->keyname && *++di->keyp)
1112             goto di_enterkey;
1113           if (di->kv.parent)
1114             goto di_leavesub;
1115           /* FALLTHROUGH */
1116
1117         case di_nextrepodata: di_nextrepodata:
1118           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1119               goto di_enterrepodata;
1120           /* FALLTHROUGH */
1121
1122         case di_nextsolvable: di_nextsolvable:
1123           if (!(di->flags & SEARCH_THISSOLVID))
1124             {
1125               if (di->solvid < 0)
1126                 di->solvid = di->repo->start;
1127               else
1128                 di->solvid++;
1129               for (; di->solvid < di->repo->end; di->solvid++)
1130                 {
1131                   if (di->pool->solvables[di->solvid].repo == di->repo)
1132                     goto di_entersolvable;
1133                 }
1134             }
1135           /* FALLTHROUGH */
1136
1137         case di_nextrepo:
1138           if (di->repoid >= 0)
1139             {
1140               di->repoid++;
1141               if (di->repoid < di->pool->nrepos)
1142                 {
1143                   di->repo = di->pool->repos[di->repoid];
1144                   goto di_enterrepo;
1145                 }
1146             }
1147         /* FALLTHROUGH */
1148
1149         case di_bye:
1150           di->state = di_bye;
1151           return 0;
1152
1153         case di_enterarray: di_enterarray:
1154           if (di->key->name == REPOSITORY_SOLVABLES)
1155             goto di_nextkey;
1156           di->ddp = data_read_id(di->ddp, &di->kv.num);
1157           di->kv.eof = 0;
1158           di->kv.entry = -1;
1159           /* FALLTHROUGH */
1160
1161         case di_nextarrayelement: di_nextarrayelement:
1162           di->kv.entry++;
1163           if (di->kv.entry)
1164             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1165           if (di->kv.entry == di->kv.num)
1166             {
1167               if (di->nparents < di->nkeynames)
1168                 goto di_nextkey;
1169               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1170                 goto di_nextkey;
1171               di->kv.str = (char *)di->ddp;
1172               di->kv.eof = 2;
1173               di->state = di_nextkey;
1174               break;
1175             }
1176           if (di->kv.entry == di->kv.num - 1)
1177             di->kv.eof = 1;
1178           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1179             di->ddp = data_read_id(di->ddp, &di->kv.id);
1180           di->kv.str = (char *)di->ddp;
1181           if (di->nparents < di->nkeynames)
1182             goto di_entersub;
1183           if ((di->flags & SEARCH_SUB) != 0)
1184             di->state = di_entersub;
1185           else
1186             di->state = di_nextarrayelement;
1187           break;
1188
1189         case di_entersub: di_entersub:
1190           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1191             goto di_nextarrayelement;   /* sorry, full */
1192           di->parents[di->nparents].kv = di->kv;
1193           di->parents[di->nparents].dp = di->dp;
1194           di->parents[di->nparents].keyp = di->keyp;
1195           di->dp = (unsigned char *)di->kv.str;
1196           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1197           memset(&di->kv, 0, sizeof(di->kv));
1198           di->kv.parent = &di->parents[di->nparents].kv;
1199           di->nparents++;
1200           di->keyname = di->keynames[di->nparents];
1201           goto di_enterschema;
1202
1203         case di_leavesub: di_leavesub:
1204           di->nparents--;
1205           di->dp = di->parents[di->nparents].dp;
1206           di->kv = di->parents[di->nparents].kv;
1207           di->keyp = di->parents[di->nparents].keyp;
1208           di->key = di->data->keys + *di->keyp;
1209           di->ddp = (unsigned char *)di->kv.str;
1210           di->keyname = di->keynames[di->nparents];
1211           goto di_nextarrayelement;
1212
1213         /* special solvable attr handling follows */
1214
1215         case di_nextsolvableattr:
1216           di->kv.id = *di->idp++;
1217           di->kv.entry++;
1218           if (!*di->idp)
1219             {
1220               di->kv.eof = 1;
1221               di->state = di_nextsolvablekey;
1222             }
1223           break;
1224
1225         case di_nextsolvablekey: di_nextsolvablekey:
1226           if (di->keyname || di->key->name == RPM_RPMDBID)
1227             goto di_enterrepodata;
1228           di->key++;
1229           /* FALLTHROUGH */
1230
1231         case di_entersolvablekey: di_entersolvablekey:
1232           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1233           if (!di->idp || !di->idp[0])
1234             goto di_nextsolvablekey;
1235           di->kv.id = di->idp[0];
1236           di->kv.num = di->idp[0];
1237           if (!di->kv.eof && !di->idp[1])
1238             di->kv.eof = 1;
1239           di->kv.entry = 0;
1240           if (di->kv.eof)
1241             di->state = di_nextsolvablekey;
1242           else
1243             di->state = di_nextsolvableattr;
1244           break;
1245         }
1246
1247       if (di->matcher.match)
1248         {
1249           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1250             {
1251               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1252                 return 1;
1253               continue;
1254             }
1255           if (!datamatcher_match(&di->matcher, di->kv.str))
1256           continue;
1257         }
1258       /* found something! */
1259       return 1;
1260     }
1261 }
1262
1263 void
1264 dataiterator_entersub(Dataiterator *di)
1265 {
1266   if (di->state == di_nextarrayelement)
1267     di->state = di_entersub;
1268 }
1269
1270 void
1271 dataiterator_setpos(Dataiterator *di)
1272 {
1273   if (di->kv.eof)
1274     {
1275       pool_clear_pos(di->pool);
1276       return;
1277     }
1278   di->pool->pos.solvid = di->solvid;
1279   di->pool->pos.repo = di->repo;
1280   di->pool->pos.repodataid = di->data - di->repo->repodata;
1281   di->pool->pos.schema = di->kv.id;
1282   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1283 }
1284
1285 void
1286 dataiterator_setpos_parent(Dataiterator *di)
1287 {
1288   if (!di->kv.parent)
1289     {
1290       pool_clear_pos(di->pool);
1291       return;
1292     }
1293   di->pool->pos.solvid = di->solvid;
1294   di->pool->pos.repo = di->repo;
1295   di->pool->pos.repodataid = di->data - di->repo->repodata;
1296   di->pool->pos.schema = di->kv.parent->id;
1297   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1298 }
1299
1300 void
1301 dataiterator_skip_attribute(Dataiterator *di)
1302 {
1303   if (di->state == di_nextsolvableattr)
1304     di->state = di_nextsolvablekey;
1305   else
1306     di->state = di_nextkey;
1307 }
1308
1309 void
1310 dataiterator_skip_solvable(Dataiterator *di)
1311 {
1312   di->state = di_nextsolvable;
1313 }
1314
1315 void
1316 dataiterator_skip_repo(Dataiterator *di)
1317 {
1318   di->state = di_nextrepo;
1319 }
1320
1321 void
1322 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1323 {
1324   di->nparents = 0;
1325   if (solvid == SOLVID_POS)
1326     {
1327       di->repo = di->pool->pos.repo;
1328       if (!di->repo)
1329         {
1330           di->state = di_bye;
1331           return;
1332         }
1333       di->repoid = -1;
1334       di->data = di->repo->repodata + di->pool->pos.repodataid;
1335       di->repodataid = -1;
1336       di->solvid = di->pool->pos.solvid;
1337       di->state = di_enterrepo;
1338       di->flags |= SEARCH_THISSOLVID;
1339       return;
1340     }
1341   if (solvid > 0)
1342     {
1343       di->repo = di->pool->solvables[solvid].repo;
1344       di->repoid = -1;
1345     }
1346   else if (di->repoid >= 0)
1347     {
1348       if (!di->pool->nrepos)
1349         {
1350           di->state = di_bye;
1351           return;
1352         }
1353       di->repo = di->pool->repos[0];
1354       di->repoid = 0;
1355     }
1356   di->repodataid = 0;
1357   di->solvid = solvid;
1358   if (solvid)
1359     di->flags |= SEARCH_THISSOLVID;
1360   di->state = di_entersolvable;
1361 }
1362
1363 void
1364 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1365 {
1366   di->nparents = 0;
1367   di->repo = repo;
1368   di->repoid = -1;
1369   di->repodataid = 0;
1370   di->solvid = 0;
1371   di->flags &= ~SEARCH_THISSOLVID;
1372   di->state = di_enterrepo;
1373 }
1374
1375 int
1376 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1377 {
1378   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1379     return 0;
1380   return datamatcher_match(ma, di->kv.str);
1381 }
1382
1383 int
1384 dataiterator_match_obsolete(Dataiterator *di, int flags, const void *vmatch)
1385 {
1386   Datamatcher matcher;
1387
1388   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, flags))
1389     return 0;
1390   matcher = di->matcher;
1391   matcher.flags = flags;
1392   matcher.match = (void *)vmatch;
1393   return datamatcher_match(&matcher, di->kv.str);
1394 }
1395
1396
1397 /************************************************************************
1398  * data modify functions
1399  */
1400
1401 /* extend repodata so that it includes solvables p */
1402 void
1403 repodata_extend(Repodata *data, Id p)
1404 {
1405   if (data->start == data->end)
1406     data->start = data->end = p;
1407   if (p >= data->end)
1408     {
1409       int old = data->end - data->start;
1410       int new = p - data->end + 1;
1411       if (data->attrs)
1412         {
1413           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1414           memset(data->attrs + old, 0, new * sizeof(Id *));
1415         }
1416       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1417       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1418       data->end = p + 1;
1419     }
1420   if (p < data->start)
1421     {
1422       int old = data->end - data->start;
1423       int new = data->start - p;
1424       if (data->attrs)
1425         {
1426           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1427           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1428           memset(data->attrs, 0, new * sizeof(Id *));
1429         }
1430       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1431       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1432       memset(data->incoreoffset, 0, new * sizeof(Id));
1433       data->start = p;
1434     }
1435 }
1436
1437 void
1438 repodata_extend_block(Repodata *data, Id start, Id num)
1439 {
1440   if (!num)
1441     return;
1442   if (!data->incoreoffset)
1443     {
1444       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1445       data->start = start;
1446       data->end = start + num;
1447       return;
1448     }
1449   repodata_extend(data, start);
1450   if (num > 1)
1451     repodata_extend(data, start + num - 1);
1452 }
1453
1454 /**********************************************************************/
1455
1456 #define REPODATA_ATTRS_BLOCK 63
1457 #define REPODATA_ATTRDATA_BLOCK 1023
1458 #define REPODATA_ATTRIDDATA_BLOCK 63
1459
1460
1461 Id
1462 repodata_new_handle(Repodata *data)
1463 {
1464   if (!data->nxattrs)
1465     {
1466       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1467       data->nxattrs = 2;
1468     }
1469   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1470   data->xattrs[data->nxattrs] = 0;
1471   return -(data->nxattrs++);
1472 }
1473
1474 static inline Id **
1475 repodata_get_attrp(Repodata *data, Id handle)
1476 {
1477   if (handle == SOLVID_META)
1478     {
1479       if (!data->xattrs)
1480         {
1481           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1482           data->nxattrs = 2;
1483         }
1484     }
1485   if (handle < 0)
1486     return data->xattrs - handle;
1487   if (handle < data->start || handle >= data->end)
1488     repodata_extend(data, handle);
1489   if (!data->attrs)
1490     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1491   return data->attrs + (handle - data->start);
1492 }
1493
1494 static void
1495 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1496 {
1497   Id *pp;
1498   Id *ap, **app;
1499   int i;
1500
1501   app = repodata_get_attrp(data, handle);
1502   ap = *app;
1503   i = 0;
1504   if (ap)
1505     {
1506       for (pp = ap; *pp; pp += 2)
1507         /* Determine equality based on the name only, allows us to change
1508            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1509         if (data->keys[*pp].name == data->keys[keyid].name)
1510           break;
1511       if (*pp)
1512         {
1513           if (overwrite)
1514             {
1515               pp[0] = keyid;
1516               pp[1] = val;
1517             }
1518           return;
1519         }
1520       i = pp - ap;
1521     }
1522   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1523   *app = ap;
1524   pp = ap + i;
1525   *pp++ = keyid;
1526   *pp++ = val;
1527   *pp = 0;
1528 }
1529
1530
1531 void
1532 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1533 {
1534   Id keyid;
1535
1536   keyid = repodata_key2id(data, key, 1);
1537   repodata_insert_keyid(data, solvid, keyid, val, 1);
1538 }
1539
1540 void
1541 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1542 {
1543   Repokey key;
1544   key.name = keyname;
1545   key.type = REPOKEY_TYPE_ID;
1546   key.size = 0;
1547   key.storage = KEY_STORAGE_INCORE;
1548   repodata_set(data, solvid, &key, id);
1549 }
1550
1551 void
1552 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1553 {
1554   Repokey key;
1555   key.name = keyname;
1556   key.type = REPOKEY_TYPE_NUM;
1557   key.size = 0;
1558   key.storage = KEY_STORAGE_INCORE;
1559   repodata_set(data, solvid, &key, (Id)num);
1560 }
1561
1562 void
1563 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
1564 {
1565   Repokey key;
1566   Id id;
1567   if (data->localpool)
1568     id = stringpool_str2id(&data->spool, str, 1);
1569   else
1570     id = str2id(data->repo->pool, str, 1);
1571   key.name = keyname;
1572   key.type = REPOKEY_TYPE_ID;
1573   key.size = 0;
1574   key.storage = KEY_STORAGE_INCORE;
1575   repodata_set(data, solvid, &key, id);
1576 }
1577
1578 void
1579 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
1580 {
1581   Repokey key;
1582   key.name = keyname;
1583   key.type = REPOKEY_TYPE_CONSTANT;
1584   key.size = constant;
1585   key.storage = KEY_STORAGE_INCORE;
1586   repodata_set(data, solvid, &key, 0);
1587 }
1588
1589 void
1590 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
1591 {
1592   Repokey key;
1593   key.name = keyname;
1594   key.type = REPOKEY_TYPE_CONSTANTID;
1595   key.size = id;
1596   key.storage = KEY_STORAGE_INCORE;
1597   repodata_set(data, solvid, &key, 0);
1598 }
1599
1600 void
1601 repodata_set_void(Repodata *data, Id solvid, Id keyname)
1602 {
1603   Repokey key;
1604   key.name = keyname;
1605   key.type = REPOKEY_TYPE_VOID;
1606   key.size = 0;
1607   key.storage = KEY_STORAGE_INCORE;
1608   repodata_set(data, solvid, &key, 0);
1609 }
1610
1611 void
1612 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
1613 {
1614   Repokey key;
1615   int l;
1616
1617   l = strlen(str) + 1;
1618   key.name = keyname;
1619   key.type = REPOKEY_TYPE_STR;
1620   key.size = 0;
1621   key.storage = KEY_STORAGE_INCORE;
1622   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1623   memcpy(data->attrdata + data->attrdatalen, str, l);
1624   repodata_set(data, solvid, &key, data->attrdatalen);
1625   data->attrdatalen += l;
1626 }
1627
1628 static void
1629 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1630 {
1631   int oldsize;
1632   Id *ida, *pp, **ppp;
1633
1634   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1635     {
1636       /* great! just append the new data */
1637       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1638       data->attriddatalen--;    /* overwrite terminating 0  */
1639       data->lastdatalen += entrysize;
1640       return;
1641     }
1642   ppp = repodata_get_attrp(data, handle);
1643   pp = *ppp;
1644   if (pp)
1645     for (; *pp; pp += 2)
1646       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1647         break;
1648   if (!pp || !*pp)
1649     {
1650       /* not found. allocate new key */
1651       Repokey key;
1652       key.name = keyname;
1653       key.type = keytype;
1654       key.size = 0;
1655       key.storage = KEY_STORAGE_INCORE;
1656       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1657       repodata_set(data, handle, &key, data->attriddatalen);
1658       data->lasthandle = 0;     /* next time... */
1659       return;
1660     }
1661   oldsize = 0;
1662   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1663     oldsize += entrysize;
1664   if (ida + 1 == data->attriddata + data->attriddatalen)
1665     {
1666       /* this was the last entry, just append it */
1667       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1668       data->attriddatalen--;    /* overwrite terminating 0  */
1669     }
1670   else
1671     {
1672       /* too bad. move to back. */
1673       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1674       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1675       pp[1] = data->attriddatalen;
1676       data->attriddatalen += oldsize;
1677     }
1678   data->lasthandle = handle;
1679   data->lastkey = *pp;
1680   data->lastdatalen = data->attriddatalen + entrysize + 1;
1681 }
1682
1683 static inline int
1684 checksumtype2len(Id type)
1685 {
1686   switch (type)
1687     {
1688     case REPOKEY_TYPE_MD5:
1689       return SIZEOF_MD5;
1690     case REPOKEY_TYPE_SHA1:
1691       return SIZEOF_SHA1;
1692     case REPOKEY_TYPE_SHA256:
1693       return SIZEOF_SHA256;
1694     default:
1695       return 0;
1696     }
1697 }
1698
1699 void
1700 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1701                       const unsigned char *str)
1702 {
1703   Repokey key;
1704   int l = checksumtype2len(type);
1705
1706   if (!l)
1707     return;
1708   key.name = keyname;
1709   key.type = type;
1710   key.size = 0;
1711   key.storage = KEY_STORAGE_INCORE;
1712   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1713   memcpy(data->attrdata + data->attrdatalen, str, l);
1714   repodata_set(data, solvid, &key, data->attrdatalen);
1715   data->attrdatalen += l;
1716 }
1717
1718 static int
1719 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1720 {
1721   int i;
1722   for (i = 0; i < buflen; i++)
1723     {
1724 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1725                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1726                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1727                 : -1)
1728       int v = c2h(*str);
1729       str++;
1730       if (v < 0)
1731         return 0;
1732       buf[i] = v;
1733       v = c2h(*str);
1734       str++;
1735       if (v < 0)
1736         return 0;
1737       buf[i] = (buf[i] << 4) | v;
1738 #undef c2h
1739     }
1740   return buflen;
1741 }
1742
1743 void
1744 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1745                       const char *str)
1746 {
1747   unsigned char buf[64];
1748   int l = checksumtype2len(type);
1749
1750   if (!l)
1751     return;
1752   if (hexstr2bytes(buf, str, l) != l)
1753     {
1754       fprintf(stderr, "Invalid hex character in '%s'\n", str);
1755       return;
1756     }
1757   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
1758 }
1759
1760 const char *
1761 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1762 {
1763   int i, l;
1764   char *str, *s;
1765
1766   l = checksumtype2len(type);
1767   if (!l)
1768     return "";
1769   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1770   for (i = 0; i < l; i++)
1771     {
1772       unsigned char v = buf[i];
1773       unsigned char w = v >> 4;
1774       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1775       w = v & 15;
1776       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1777     }
1778   *s = 0;
1779   return str;
1780 }
1781
1782 Id
1783 repodata_globalize_id(Repodata *data, Id id)
1784 {
1785   if (!data || !data->localpool)
1786     return id;
1787   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1788 }
1789
1790 void
1791 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
1792 {
1793   assert(dir);
1794 #if 0
1795 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
1796 #endif
1797   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1798   data->attriddata[data->attriddatalen++] = dir;
1799   data->attriddata[data->attriddatalen++] = num;
1800   data->attriddata[data->attriddatalen++] = num2;
1801   data->attriddata[data->attriddatalen++] = 0;
1802 }
1803
1804 void
1805 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
1806 {
1807   Id stroff;
1808   int l;
1809
1810   assert(dir);
1811   l = strlen(str) + 1;
1812   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1813   memcpy(data->attrdata + data->attrdatalen, str, l);
1814   stroff = data->attrdatalen;
1815   data->attrdatalen += l;
1816
1817 #if 0
1818 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
1819 #endif
1820   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1821   data->attriddata[data->attriddatalen++] = dir;
1822   data->attriddata[data->attriddatalen++] = stroff;
1823   data->attriddata[data->attriddatalen++] = 0;
1824 }
1825
1826 void
1827 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
1828 {
1829 #if 0
1830 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
1831 #endif
1832   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
1833   data->attriddata[data->attriddatalen++] = id;
1834   data->attriddata[data->attriddatalen++] = 0;
1835 }
1836
1837 void
1838 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
1839                            const char *str)
1840 {
1841   Id id;
1842   if (data->localpool)
1843     id = stringpool_str2id(&data->spool, str, 1);
1844   else
1845     id = str2id(data->repo->pool, str, 1);
1846   repodata_add_idarray(data, solvid, keyname, id);
1847 }
1848
1849 void
1850 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1851 {
1852   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
1853   data->attriddata[data->attriddatalen++] = ghandle;
1854   data->attriddata[data->attriddatalen++] = 0;
1855 }
1856
1857 void
1858 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1859 {
1860   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
1861   data->attriddata[data->attriddatalen++] = ghandle;
1862   data->attriddata[data->attriddatalen++] = 0;
1863 }
1864
1865 void
1866 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1867 {
1868   Id *keyp;
1869   if (dest == src || !(keyp = data->attrs[src]))
1870     return;
1871   for (; *keyp; keyp += 2)
1872     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1873 }
1874
1875
1876
1877
1878 /**********************************************************************/
1879
1880 /* unify with repo_write! */
1881
1882 #define EXTDATA_BLOCK 1023
1883
1884 struct extdata {
1885   unsigned char *buf;
1886   int len;
1887 };
1888
1889 static void
1890 data_addid(struct extdata *xd, Id x)
1891 {
1892   unsigned char *dp;
1893   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1894   dp = xd->buf + xd->len;
1895
1896   if (x >= (1 << 14))
1897     {
1898       if (x >= (1 << 28))
1899         *dp++ = (x >> 28) | 128;
1900       if (x >= (1 << 21))
1901         *dp++ = (x >> 21) | 128;
1902       *dp++ = (x >> 14) | 128;
1903     }
1904   if (x >= (1 << 7))
1905     *dp++ = (x >> 7) | 128;
1906   *dp++ = x & 127;
1907   xd->len = dp - xd->buf;
1908 }
1909
1910 static void
1911 data_addideof(struct extdata *xd, Id x, int eof)
1912 {
1913   if (x >= 64)
1914     x = (x & 63) | ((x & ~63) << 1);
1915   data_addid(xd, (eof ? x: x | 64));
1916 }
1917
1918 static void
1919 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1920 {
1921   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1922   memcpy(xd->buf + xd->len, blob, len);
1923   xd->len += len;
1924 }
1925
1926 /*********************************/
1927
1928 static void
1929 repodata_serialize_key(Repodata *data, struct extdata *newincore,
1930                        struct extdata *newvincore,
1931                        Id *schema,
1932                        Repokey *key, Id val)
1933 {
1934   /* Otherwise we have a new value.  Parse it into the internal
1935      form.  */
1936   Id *ida;
1937   struct extdata *xd;
1938   unsigned int oldvincorelen = 0;
1939   Id schemaid, *sp;
1940
1941   xd = newincore;
1942   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1943     {
1944       xd = newvincore;
1945       oldvincorelen = xd->len;
1946     }
1947   switch (key->type)
1948     {
1949     case REPOKEY_TYPE_VOID:
1950     case REPOKEY_TYPE_CONSTANT:
1951     case REPOKEY_TYPE_CONSTANTID:
1952       break;
1953     case REPOKEY_TYPE_STR:
1954       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
1955       break;
1956     case REPOKEY_TYPE_MD5:
1957       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
1958       break;
1959     case REPOKEY_TYPE_SHA1:
1960       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
1961       break;
1962     case REPOKEY_TYPE_SHA256:
1963       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
1964       break;
1965     case REPOKEY_TYPE_ID:
1966     case REPOKEY_TYPE_NUM:
1967     case REPOKEY_TYPE_DIR:
1968       data_addid(xd, val);
1969       break;
1970     case REPOKEY_TYPE_IDARRAY:
1971       for (ida = data->attriddata + val; *ida; ida++)
1972         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1973       break;
1974     case REPOKEY_TYPE_DIRNUMNUMARRAY:
1975       for (ida = data->attriddata + val; *ida; ida += 3)
1976         {
1977           data_addid(xd, ida[0]);
1978           data_addid(xd, ida[1]);
1979           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1980         }
1981       break;
1982     case REPOKEY_TYPE_DIRSTRARRAY:
1983       for (ida = data->attriddata + val; *ida; ida += 2)
1984         {
1985           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1986           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1987         }
1988       break;
1989     case REPOKEY_TYPE_FIXARRAY:
1990       {
1991         int num = 0;
1992         schemaid = 0;
1993         for (ida = data->attriddata + val; *ida; ida++)
1994           {
1995 #if 0
1996             fprintf(stderr, "serialize struct %d\n", *ida);
1997 #endif
1998             sp = schema;
1999             Id *kp = data->xattrs[-*ida];
2000             if (!kp)
2001               continue;
2002             num++;
2003             for (;*kp; kp += 2)
2004               {
2005 #if 0
2006                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2007 #endif
2008                 *sp++ = *kp;
2009               }
2010             *sp = 0;
2011             if (!schemaid)
2012               schemaid = repodata_schema2id(data, schema, 1);
2013             else if (schemaid != repodata_schema2id(data, schema, 0))
2014               {
2015                 fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
2016                 exit(1);
2017               }
2018 #if 0
2019             fprintf(stderr, "  schema %d\n", schemaid);
2020 #endif
2021           }
2022         if (!num)
2023           break;
2024         data_addid(xd, num);
2025         data_addid(xd, schemaid);
2026         for (ida = data->attriddata + val; *ida; ida++)
2027           {
2028             Id *kp = data->xattrs[-*ida];
2029             if (!kp)
2030               continue;
2031             for (;*kp; kp += 2)
2032               {
2033                 repodata_serialize_key(data, newincore, newvincore,
2034                                        schema, data->keys + *kp, kp[1]);
2035               }
2036           }
2037         break;
2038       }
2039     case REPOKEY_TYPE_FLEXARRAY:
2040       {
2041         int num = 0;
2042         for (ida = data->attriddata + val; *ida; ida++)
2043           num++;
2044         data_addid(xd, num);
2045         for (ida = data->attriddata + val; *ida; ida++)
2046           {
2047             Id *kp = data->xattrs[-*ida];
2048             if (!kp)
2049               {
2050                 data_addid(xd, 0);      /* XXX */
2051                 continue;
2052               }
2053             sp = schema;
2054             for (;*kp; kp += 2)
2055               *sp++ = *kp;
2056             *sp = 0;
2057             schemaid = repodata_schema2id(data, schema, 1);
2058             data_addid(xd, schemaid);
2059             kp = data->xattrs[-*ida];
2060             for (;*kp; kp += 2)
2061               {
2062                 repodata_serialize_key(data, newincore, newvincore,
2063                                        schema, data->keys + *kp, kp[1]);
2064               }
2065           }
2066         break;
2067       }
2068     default:
2069       fprintf(stderr, "don't know how to handle type %d\n", key->type);
2070       exit(1);
2071     }
2072   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2073     {
2074       /* put offset/len in incore */
2075       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2076       oldvincorelen = xd->len - oldvincorelen;
2077       data_addid(newincore, oldvincorelen);
2078     }
2079 }
2080
2081 void
2082 repodata_internalize(Repodata *data)
2083 {
2084   Repokey *key, solvkey;
2085   Id entry, nentry;
2086   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2087   unsigned char *dp, *ndp;
2088   int newschema, oldcount;
2089   struct extdata newincore;
2090   struct extdata newvincore;
2091   Id solvkeyid;
2092
2093   if (!data->attrs && !data->xattrs)
2094     return;
2095
2096   newvincore.buf = data->vincore;
2097   newvincore.len = data->vincorelen;
2098
2099   /* find the solvables key, create if needed */
2100   memset(&solvkey, 0, sizeof(solvkey));
2101   solvkey.name = REPOSITORY_SOLVABLES;
2102   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2103   solvkey.size = 0;
2104   solvkey.storage = KEY_STORAGE_INCORE;
2105   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2106
2107   schema = sat_malloc2(data->nkeys, sizeof(Id));
2108   seen = sat_malloc2(data->nkeys, sizeof(Id));
2109
2110   /* Merge the data already existing (in data->schemata, ->incoredata and
2111      friends) with the new attributes in data->attrs[].  */
2112   nentry = data->end - data->start;
2113   memset(&newincore, 0, sizeof(newincore));
2114   data_addid(&newincore, 0);    /* start data at offset 1 */
2115
2116   data->mainschema = 0;
2117   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2118
2119   /* join entry data */
2120   /* we start with the meta data, entry -1 */
2121   for (entry = -1; entry < nentry; entry++)
2122     {
2123       memset(seen, 0, data->nkeys * sizeof(Id));
2124       oldschema = 0;
2125       dp = data->incoredata;
2126       if (dp)
2127         {
2128           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2129           dp = data_read_id(dp, &oldschema);
2130         }
2131 #if 0
2132 fprintf(stderr, "oldschema %d\n", oldschema);
2133 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2134 fprintf(stderr, "schemadata %p\n", data->schemadata);
2135 #endif
2136       /* seen: -1: old data  0: skipped  >0: id + 1 */
2137       newschema = 0;
2138       oldcount = 0;
2139       sp = schema;
2140       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2141         {
2142           if (seen[*keyp])
2143             {
2144               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
2145               exit(1);
2146             }
2147           seen[*keyp] = -1;
2148           *sp++ = *keyp;
2149           oldcount++;
2150         }
2151       if (entry >= 0)
2152         keyp = data->attrs ? data->attrs[entry] : 0;
2153       else
2154         {
2155           /* strip solvables key */
2156           *sp = 0;
2157           for (sp = keyp = schema; *sp; sp++)
2158             if (*sp != solvkeyid)
2159               *keyp++ = *sp;
2160             else
2161               oldcount--;
2162           sp = keyp;
2163           seen[solvkeyid] = 0;
2164           keyp = data->xattrs ? data->xattrs[1] : 0;
2165         }
2166       if (keyp)
2167         for (; *keyp; keyp += 2)
2168           {
2169             if (!seen[*keyp])
2170               {
2171                 newschema = 1;
2172                 *sp++ = *keyp;
2173               }
2174             seen[*keyp] = keyp[1] + 1;
2175           }
2176       if (entry < 0 && data->end != data->start)
2177         {
2178           *sp++ = solvkeyid;
2179           newschema = 1;
2180         }
2181       *sp = 0;
2182       if (newschema)
2183         /* Ideally we'd like to sort the new schema here, to ensure
2184            schema equality independend of the ordering.  We can't do that
2185            yet.  For once see below (old ids need to come before new ids).
2186            An additional difficulty is that we also need to move
2187            the values with the keys.  */
2188         schemaid = repodata_schema2id(data, schema, 1);
2189       else
2190         schemaid = oldschema;
2191
2192
2193       /* Now create data blob.  We walk through the (possibly new) schema
2194          and either copy over old data, or insert the new.  */
2195       /* XXX Here we rely on the fact that the (new) schema has the form
2196          o1 o2 o3 o4 ... | n1 n2 n3 ...
2197          (oX being the old keyids (possibly overwritten), and nX being
2198           the new keyids).  This rules out sorting the keyids in order
2199          to ensure a small schema count.  */
2200       if (entry >= 0)
2201         data->incoreoffset[entry] = newincore.len;
2202       data_addid(&newincore, schemaid);
2203       if (entry == -1)
2204         {
2205           data->mainschema = schemaid;
2206           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2207         }
2208       keypstart = data->schemadata + data->schemata[schemaid];
2209       for (keyp = keypstart; *keyp; keyp++)
2210         {
2211           if (entry == -1)
2212             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2213           if (*keyp == solvkeyid)
2214             {
2215               /* add flexarray entry count */
2216               data_addid(&newincore, data->end - data->start);
2217               break;
2218             }
2219           key = data->keys + *keyp;
2220 #if 0
2221           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2222 #endif
2223           ndp = dp;
2224           if (oldcount)
2225             {
2226               /* Skip the data associated with this old key.  */
2227               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2228                 {
2229                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2230                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2231                 }
2232               else if (key->storage == KEY_STORAGE_INCORE)
2233                 ndp = data_skip_key(data, dp, key);
2234               oldcount--;
2235             }
2236           if (seen[*keyp] == -1)
2237             {
2238               /* If this key was an old one _and_ was not overwritten with
2239                  a different value copy over the old value (we skipped it
2240                  above).  */
2241               if (dp != ndp)
2242                 data_addblob(&newincore, dp, ndp - dp);
2243               seen[*keyp] = 0;
2244             }
2245           else if (seen[*keyp])
2246             {
2247               /* Otherwise we have a new value.  Parse it into the internal
2248                  form.  */
2249               repodata_serialize_key(data, &newincore, &newvincore,
2250                                      schema, key, seen[*keyp] - 1);
2251             }
2252           dp = ndp;
2253         }
2254       if (entry >= 0 && data->attrs && data->attrs[entry])
2255         data->attrs[entry] = sat_free(data->attrs[entry]);
2256     }
2257   /* free all xattrs */
2258   for (entry = 0; entry < data->nxattrs; entry++)
2259     if (data->xattrs[entry])
2260       sat_free(data->xattrs[entry]);
2261   data->xattrs = sat_free(data->xattrs);
2262   data->nxattrs = 0;
2263
2264   data->lasthandle = 0;
2265   data->lastkey = 0;
2266   data->lastdatalen = 0;
2267   sat_free(schema);
2268   sat_free(seen);
2269   repodata_free_schemahash(data);
2270
2271   sat_free(data->incoredata);
2272   data->incoredata = newincore.buf;
2273   data->incoredatalen = newincore.len;
2274   data->incoredatafree = 0;
2275
2276   sat_free(data->vincore);
2277   data->vincore = newvincore.buf;
2278   data->vincorelen = newvincore.len;
2279
2280   data->attrs = sat_free(data->attrs);
2281   data->attrdata = sat_free(data->attrdata);
2282   data->attriddata = sat_free(data->attriddata);
2283   data->attrdatalen = 0;
2284   data->attriddatalen = 0;
2285 }
2286
2287 void
2288 repodata_disable_paging(Repodata *data)
2289 {
2290   if (maybe_load_repodata(data, 0))
2291     repopagestore_disable_paging(&data->store);
2292 }
2293
2294 /*
2295 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2296 */