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