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