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