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