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