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