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