4991e4587d4181ec5b3ef0080e9c970cfe9c3c51
[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 static inline const char *
1789 evrid2vrstr(Pool *pool, Id evrid)
1790 {
1791   const char *p, *evr = id2str(pool, evrid);
1792   if (!evr) 
1793     return evr;
1794   for (p = evr; *p >= '0' && *p <= '9'; p++)
1795     ; 
1796   return p != evr && *p == ':' ? p + 1 : evr;
1797 }
1798
1799 void
1800 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
1801 {
1802   Pool *pool = data->repo->pool;
1803   Solvable *s;
1804   const char *str, *fp;
1805   int l = 0;
1806
1807   if (medianr)
1808     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
1809   if (!dir)
1810     {
1811       if ((dir = strrchr(file, '/')) != 0)
1812         {
1813           l = dir - file;
1814           dir = file;
1815           file = dir + l + 1;
1816           if (!l)
1817             l++;
1818         }
1819     }
1820   else
1821     l = strlen(dir);
1822   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
1823     {
1824       dir += 2;
1825       l -= 2;
1826     }
1827   if (l == 1 && dir[0] == '.')
1828     l = 0;
1829   s = pool->solvables + solvid;
1830   if (dir && l)
1831     {
1832       str = id2str(pool, s->arch);
1833       if (!strncmp(dir, str, l) && !str[l])
1834         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
1835       else if (!dir[l])
1836         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
1837       else
1838         {
1839           char *dir2 = strdup(dir);
1840           dir2[l] = 0;
1841           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
1842           free(dir2);
1843         }
1844     }
1845   fp = file;
1846   str = id2str(pool, s->name);
1847   l = strlen(str);
1848   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
1849     {
1850       fp += l + 1;
1851       str = evrid2vrstr(pool, s->evr);
1852       l = strlen(str);
1853       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
1854         {
1855           fp += l + 1;
1856           str = id2str(pool, s->arch);
1857           l = strlen(str);
1858           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
1859             {
1860               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
1861               return;
1862             }
1863         }
1864     }
1865   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
1866 }
1867
1868 Id
1869 repodata_globalize_id(Repodata *data, Id id)
1870 {
1871   if (!data || !data->localpool)
1872     return id;
1873   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1874 }
1875
1876 void
1877 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
1878 {
1879   assert(dir);
1880 #if 0
1881 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
1882 #endif
1883   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1884   data->attriddata[data->attriddatalen++] = dir;
1885   data->attriddata[data->attriddatalen++] = num;
1886   data->attriddata[data->attriddatalen++] = num2;
1887   data->attriddata[data->attriddatalen++] = 0;
1888 }
1889
1890 void
1891 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
1892 {
1893   Id stroff;
1894   int l;
1895
1896   assert(dir);
1897   l = strlen(str) + 1;
1898   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1899   memcpy(data->attrdata + data->attrdatalen, str, l);
1900   stroff = data->attrdatalen;
1901   data->attrdatalen += l;
1902
1903 #if 0
1904 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
1905 #endif
1906   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1907   data->attriddata[data->attriddatalen++] = dir;
1908   data->attriddata[data->attriddatalen++] = stroff;
1909   data->attriddata[data->attriddatalen++] = 0;
1910 }
1911
1912 void
1913 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
1914 {
1915 #if 0
1916 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
1917 #endif
1918   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
1919   data->attriddata[data->attriddatalen++] = id;
1920   data->attriddata[data->attriddatalen++] = 0;
1921 }
1922
1923 void
1924 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
1925                            const char *str)
1926 {
1927   Id id;
1928   if (data->localpool)
1929     id = stringpool_str2id(&data->spool, str, 1);
1930   else
1931     id = str2id(data->repo->pool, str, 1);
1932   repodata_add_idarray(data, solvid, keyname, id);
1933 }
1934
1935 void
1936 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1937 {
1938   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
1939   data->attriddata[data->attriddatalen++] = ghandle;
1940   data->attriddata[data->attriddatalen++] = 0;
1941 }
1942
1943 void
1944 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1945 {
1946   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
1947   data->attriddata[data->attriddatalen++] = ghandle;
1948   data->attriddata[data->attriddatalen++] = 0;
1949 }
1950
1951 void
1952 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1953 {
1954   Id *keyp;
1955   if (dest == src || !(keyp = data->attrs[src]))
1956     return;
1957   for (; *keyp; keyp += 2)
1958     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1959 }
1960
1961
1962
1963
1964 /**********************************************************************/
1965
1966 /* unify with repo_write! */
1967
1968 #define EXTDATA_BLOCK 1023
1969
1970 struct extdata {
1971   unsigned char *buf;
1972   int len;
1973 };
1974
1975 static void
1976 data_addid(struct extdata *xd, Id x)
1977 {
1978   unsigned char *dp;
1979   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1980   dp = xd->buf + xd->len;
1981
1982   if (x >= (1 << 14))
1983     {
1984       if (x >= (1 << 28))
1985         *dp++ = (x >> 28) | 128;
1986       if (x >= (1 << 21))
1987         *dp++ = (x >> 21) | 128;
1988       *dp++ = (x >> 14) | 128;
1989     }
1990   if (x >= (1 << 7))
1991     *dp++ = (x >> 7) | 128;
1992   *dp++ = x & 127;
1993   xd->len = dp - xd->buf;
1994 }
1995
1996 static void
1997 data_addideof(struct extdata *xd, Id x, int eof)
1998 {
1999   if (x >= 64)
2000     x = (x & 63) | ((x & ~63) << 1);
2001   data_addid(xd, (eof ? x: x | 64));
2002 }
2003
2004 static void
2005 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2006 {
2007   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2008   memcpy(xd->buf + xd->len, blob, len);
2009   xd->len += len;
2010 }
2011
2012 /*********************************/
2013
2014 static void
2015 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2016                        struct extdata *newvincore,
2017                        Id *schema,
2018                        Repokey *key, Id val)
2019 {
2020   /* Otherwise we have a new value.  Parse it into the internal
2021      form.  */
2022   Id *ida;
2023   struct extdata *xd;
2024   unsigned int oldvincorelen = 0;
2025   Id schemaid, *sp;
2026
2027   xd = newincore;
2028   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2029     {
2030       xd = newvincore;
2031       oldvincorelen = xd->len;
2032     }
2033   switch (key->type)
2034     {
2035     case REPOKEY_TYPE_VOID:
2036     case REPOKEY_TYPE_CONSTANT:
2037     case REPOKEY_TYPE_CONSTANTID:
2038       break;
2039     case REPOKEY_TYPE_STR:
2040       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2041       break;
2042     case REPOKEY_TYPE_MD5:
2043       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2044       break;
2045     case REPOKEY_TYPE_SHA1:
2046       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2047       break;
2048     case REPOKEY_TYPE_SHA256:
2049       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2050       break;
2051     case REPOKEY_TYPE_ID:
2052     case REPOKEY_TYPE_NUM:
2053     case REPOKEY_TYPE_DIR:
2054       data_addid(xd, val);
2055       break;
2056     case REPOKEY_TYPE_IDARRAY:
2057       for (ida = data->attriddata + val; *ida; ida++)
2058         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2059       break;
2060     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2061       for (ida = data->attriddata + val; *ida; ida += 3)
2062         {
2063           data_addid(xd, ida[0]);
2064           data_addid(xd, ida[1]);
2065           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2066         }
2067       break;
2068     case REPOKEY_TYPE_DIRSTRARRAY:
2069       for (ida = data->attriddata + val; *ida; ida += 2)
2070         {
2071           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2072           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2073         }
2074       break;
2075     case REPOKEY_TYPE_FIXARRAY:
2076       {
2077         int num = 0;
2078         schemaid = 0;
2079         for (ida = data->attriddata + val; *ida; ida++)
2080           {
2081 #if 0
2082             fprintf(stderr, "serialize struct %d\n", *ida);
2083 #endif
2084             sp = schema;
2085             Id *kp = data->xattrs[-*ida];
2086             if (!kp)
2087               continue;
2088             num++;
2089             for (;*kp; kp += 2)
2090               {
2091 #if 0
2092                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2093 #endif
2094                 *sp++ = *kp;
2095               }
2096             *sp = 0;
2097             if (!schemaid)
2098               schemaid = repodata_schema2id(data, schema, 1);
2099             else if (schemaid != repodata_schema2id(data, schema, 0))
2100               {
2101                 pool_debug(data->repo->pool, SAT_FATAL, "substructs with different schemas\n");
2102                 exit(1);
2103               }
2104 #if 0
2105             fprintf(stderr, "  schema %d\n", schemaid);
2106 #endif
2107           }
2108         if (!num)
2109           break;
2110         data_addid(xd, num);
2111         data_addid(xd, schemaid);
2112         for (ida = data->attriddata + val; *ida; ida++)
2113           {
2114             Id *kp = data->xattrs[-*ida];
2115             if (!kp)
2116               continue;
2117             for (;*kp; kp += 2)
2118               {
2119                 repodata_serialize_key(data, newincore, newvincore,
2120                                        schema, data->keys + *kp, kp[1]);
2121               }
2122           }
2123         break;
2124       }
2125     case REPOKEY_TYPE_FLEXARRAY:
2126       {
2127         int num = 0;
2128         for (ida = data->attriddata + val; *ida; ida++)
2129           num++;
2130         data_addid(xd, num);
2131         for (ida = data->attriddata + val; *ida; ida++)
2132           {
2133             Id *kp = data->xattrs[-*ida];
2134             if (!kp)
2135               {
2136                 data_addid(xd, 0);      /* XXX */
2137                 continue;
2138               }
2139             sp = schema;
2140             for (;*kp; kp += 2)
2141               *sp++ = *kp;
2142             *sp = 0;
2143             schemaid = repodata_schema2id(data, schema, 1);
2144             data_addid(xd, schemaid);
2145             kp = data->xattrs[-*ida];
2146             for (;*kp; kp += 2)
2147               {
2148                 repodata_serialize_key(data, newincore, newvincore,
2149                                        schema, data->keys + *kp, kp[1]);
2150               }
2151           }
2152         break;
2153       }
2154     default:
2155       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
2156       exit(1);
2157     }
2158   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2159     {
2160       /* put offset/len in incore */
2161       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2162       oldvincorelen = xd->len - oldvincorelen;
2163       data_addid(newincore, oldvincorelen);
2164     }
2165 }
2166
2167 void
2168 repodata_internalize(Repodata *data)
2169 {
2170   Repokey *key, solvkey;
2171   Id entry, nentry;
2172   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2173   unsigned char *dp, *ndp;
2174   int newschema, oldcount;
2175   struct extdata newincore;
2176   struct extdata newvincore;
2177   Id solvkeyid;
2178
2179   if (!data->attrs && !data->xattrs)
2180     return;
2181
2182   newvincore.buf = data->vincore;
2183   newvincore.len = data->vincorelen;
2184
2185   /* find the solvables key, create if needed */
2186   memset(&solvkey, 0, sizeof(solvkey));
2187   solvkey.name = REPOSITORY_SOLVABLES;
2188   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2189   solvkey.size = 0;
2190   solvkey.storage = KEY_STORAGE_INCORE;
2191   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2192
2193   schema = sat_malloc2(data->nkeys, sizeof(Id));
2194   seen = sat_malloc2(data->nkeys, sizeof(Id));
2195
2196   /* Merge the data already existing (in data->schemata, ->incoredata and
2197      friends) with the new attributes in data->attrs[].  */
2198   nentry = data->end - data->start;
2199   memset(&newincore, 0, sizeof(newincore));
2200   data_addid(&newincore, 0);    /* start data at offset 1 */
2201
2202   data->mainschema = 0;
2203   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2204
2205   /* join entry data */
2206   /* we start with the meta data, entry -1 */
2207   for (entry = -1; entry < nentry; entry++)
2208     {
2209       memset(seen, 0, data->nkeys * sizeof(Id));
2210       oldschema = 0;
2211       dp = data->incoredata;
2212       if (dp)
2213         {
2214           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2215           dp = data_read_id(dp, &oldschema);
2216         }
2217 #if 0
2218 fprintf(stderr, "oldschema %d\n", oldschema);
2219 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2220 fprintf(stderr, "schemadata %p\n", data->schemadata);
2221 #endif
2222       /* seen: -1: old data  0: skipped  >0: id + 1 */
2223       newschema = 0;
2224       oldcount = 0;
2225       sp = schema;
2226       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2227         {
2228           if (seen[*keyp])
2229             {
2230               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
2231               exit(1);
2232             }
2233           seen[*keyp] = -1;
2234           *sp++ = *keyp;
2235           oldcount++;
2236         }
2237       if (entry >= 0)
2238         keyp = data->attrs ? data->attrs[entry] : 0;
2239       else
2240         {
2241           /* strip solvables key */
2242           *sp = 0;
2243           for (sp = keyp = schema; *sp; sp++)
2244             if (*sp != solvkeyid)
2245               *keyp++ = *sp;
2246             else
2247               oldcount--;
2248           sp = keyp;
2249           seen[solvkeyid] = 0;
2250           keyp = data->xattrs ? data->xattrs[1] : 0;
2251         }
2252       if (keyp)
2253         for (; *keyp; keyp += 2)
2254           {
2255             if (!seen[*keyp])
2256               {
2257                 newschema = 1;
2258                 *sp++ = *keyp;
2259               }
2260             seen[*keyp] = keyp[1] + 1;
2261           }
2262       if (entry < 0 && data->end != data->start)
2263         {
2264           *sp++ = solvkeyid;
2265           newschema = 1;
2266         }
2267       *sp = 0;
2268       if (newschema)
2269         /* Ideally we'd like to sort the new schema here, to ensure
2270            schema equality independend of the ordering.  We can't do that
2271            yet.  For once see below (old ids need to come before new ids).
2272            An additional difficulty is that we also need to move
2273            the values with the keys.  */
2274         schemaid = repodata_schema2id(data, schema, 1);
2275       else
2276         schemaid = oldschema;
2277
2278
2279       /* Now create data blob.  We walk through the (possibly new) schema
2280          and either copy over old data, or insert the new.  */
2281       /* XXX Here we rely on the fact that the (new) schema has the form
2282          o1 o2 o3 o4 ... | n1 n2 n3 ...
2283          (oX being the old keyids (possibly overwritten), and nX being
2284           the new keyids).  This rules out sorting the keyids in order
2285          to ensure a small schema count.  */
2286       if (entry >= 0)
2287         data->incoreoffset[entry] = newincore.len;
2288       data_addid(&newincore, schemaid);
2289       if (entry == -1)
2290         {
2291           data->mainschema = schemaid;
2292           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2293         }
2294       keypstart = data->schemadata + data->schemata[schemaid];
2295       for (keyp = keypstart; *keyp; keyp++)
2296         {
2297           if (entry == -1)
2298             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2299           if (*keyp == solvkeyid)
2300             {
2301               /* add flexarray entry count */
2302               data_addid(&newincore, data->end - data->start);
2303               break;
2304             }
2305           key = data->keys + *keyp;
2306 #if 0
2307           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2308 #endif
2309           ndp = dp;
2310           if (oldcount)
2311             {
2312               /* Skip the data associated with this old key.  */
2313               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2314                 {
2315                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2316                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2317                 }
2318               else if (key->storage == KEY_STORAGE_INCORE)
2319                 ndp = data_skip_key(data, dp, key);
2320               oldcount--;
2321             }
2322           if (seen[*keyp] == -1)
2323             {
2324               /* If this key was an old one _and_ was not overwritten with
2325                  a different value copy over the old value (we skipped it
2326                  above).  */
2327               if (dp != ndp)
2328                 data_addblob(&newincore, dp, ndp - dp);
2329               seen[*keyp] = 0;
2330             }
2331           else if (seen[*keyp])
2332             {
2333               /* Otherwise we have a new value.  Parse it into the internal
2334                  form.  */
2335               repodata_serialize_key(data, &newincore, &newvincore,
2336                                      schema, key, seen[*keyp] - 1);
2337             }
2338           dp = ndp;
2339         }
2340       if (entry >= 0 && data->attrs && data->attrs[entry])
2341         data->attrs[entry] = sat_free(data->attrs[entry]);
2342     }
2343   /* free all xattrs */
2344   for (entry = 0; entry < data->nxattrs; entry++)
2345     if (data->xattrs[entry])
2346       sat_free(data->xattrs[entry]);
2347   data->xattrs = sat_free(data->xattrs);
2348   data->nxattrs = 0;
2349
2350   data->lasthandle = 0;
2351   data->lastkey = 0;
2352   data->lastdatalen = 0;
2353   sat_free(schema);
2354   sat_free(seen);
2355   repodata_free_schemahash(data);
2356
2357   sat_free(data->incoredata);
2358   data->incoredata = newincore.buf;
2359   data->incoredatalen = newincore.len;
2360   data->incoredatafree = 0;
2361
2362   sat_free(data->vincore);
2363   data->vincore = newvincore.buf;
2364   data->vincorelen = newvincore.len;
2365
2366   data->attrs = sat_free(data->attrs);
2367   data->attrdata = sat_free(data->attrdata);
2368   data->attriddata = sat_free(data->attriddata);
2369   data->attrdatalen = 0;
2370   data->attriddatalen = 0;
2371 }
2372
2373 void
2374 repodata_disable_paging(Repodata *data)
2375 {
2376   if (maybe_load_repodata(data, 0))
2377     repopagestore_disable_paging(&data->store);
2378 }
2379
2380 /*
2381 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2382 */