[ABI BREAKAGE] add support for SHA224/384/512
[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  * a repository can contain multiple repodata entries, consisting of
14  * different sets of keys and different sets of solvables
15  */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <fnmatch.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <regex.h>
26
27 #include "repo.h"
28 #include "pool.h"
29 #include "poolid_private.h"
30 #include "util.h"
31 #include "hash.h"
32 #include "chksum.h"
33
34 #include "repopack.h"
35 #include "repopage.h"
36
37 #define REPODATA_BLOCK 255
38
39 static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
40
41 void
42 repodata_initdata(Repodata *data, Repo *repo, int localpool)
43 {
44   memset(data, 0, sizeof (*data));
45   data->repodataid = data - repo->repodata;
46   data->repo = repo;
47   data->localpool = localpool;
48   if (localpool)
49     stringpool_init_empty(&data->spool);
50   /* dirpool_init(&data->dirpool);      just zeros out again */
51   data->keys = solv_calloc(1, sizeof(Repokey));
52   data->nkeys = 1;
53   data->schemata = solv_calloc(1, sizeof(Id));
54   data->schemadata = solv_calloc(1, sizeof(Id));
55   data->nschemata = 1;
56   data->schemadatalen = 1;
57   repopagestore_init(&data->store);
58 }
59
60 void
61 repodata_freedata(Repodata *data)
62 {
63   int i;
64
65   solv_free(data->keys);
66
67   solv_free(data->schemata);
68   solv_free(data->schemadata);
69   solv_free(data->schematahash);
70
71   stringpool_free(&data->spool);
72   dirpool_free(&data->dirpool);
73
74   solv_free(data->mainschemaoffsets);
75   solv_free(data->incoredata);
76   solv_free(data->incoreoffset);
77   solv_free(data->verticaloffset);
78
79   repopagestore_free(&data->store);
80
81   solv_free(data->vincore);
82
83   if (data->attrs)
84     for (i = 0; i < data->end - data->start; i++)
85       solv_free(data->attrs[i]);
86   solv_free(data->attrs);
87   if (data->xattrs)
88     for (i = 0; i < data->nxattrs; i++)
89       solv_free(data->xattrs[i]);
90   solv_free(data->xattrs);
91
92   solv_free(data->attrdata);
93   solv_free(data->attriddata);
94   solv_free(data->attrnum64data);
95
96   solv_free(data->dircache);
97 }
98
99 void
100 repodata_free(Repodata *data)
101 {
102   Repo *repo = data->repo;
103   int i = data - repo->repodata;
104   if (i == 0)
105     return;
106   repodata_freedata(data);
107   if (i < repo->nrepodata - 1)
108     {
109       /* whoa! this changes the repodataids! */
110       memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
111       for (; i < repo->nrepodata - 1; i++)
112         repo->repodata[i].repodataid = i;
113     }
114   repo->nrepodata--;
115   if (repo->nrepodata == 1)
116     {
117       repo->repodata = solv_free(repo->repodata);
118       repo->nrepodata = 0;
119     }
120 }
121
122 void
123 repodata_empty(Repodata *data, int localpool)
124 {
125   void (*loadcallback)(Repodata *) = data->loadcallback;
126   int state = data->state;
127   repodata_freedata(data);
128   repodata_initdata(data, data->repo, localpool);
129   data->state = state;
130   data->loadcallback = loadcallback;
131 }
132
133
134 /***************************************************************
135  * key pool management
136  */
137
138 /* this is not so time critical that we need a hash, so we do a simple
139  * linear search */
140 Id
141 repodata_key2id(Repodata *data, Repokey *key, int create)
142 {
143   Id keyid;
144
145   for (keyid = 1; keyid < data->nkeys; keyid++)
146     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
147       {
148         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
149           continue;
150         break;
151       }
152   if (keyid == data->nkeys)
153     {
154       if (!create)
155         return 0;
156       /* allocate new key */
157       data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
158       data->keys[data->nkeys++] = *key;
159       if (data->verticaloffset)
160         {
161           data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
162           data->verticaloffset[data->nkeys - 1] = 0;
163         }
164       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
165     }
166   return keyid;
167 }
168
169
170 /***************************************************************
171  * schema pool management
172  */
173
174 #define SCHEMATA_BLOCK 31
175 #define SCHEMATADATA_BLOCK 255
176
177 Id
178 repodata_schema2id(Repodata *data, Id *schema, int create)
179 {
180   int h, len, i;
181   Id *sp, cid;
182   Id *schematahash;
183
184   if (!*schema)
185     return 0;   /* XXX: allow empty schema? */
186   if ((schematahash = data->schematahash) == 0)
187     {
188       data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
189       for (i = 1; i < data->nschemata; i++)
190         {
191           for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
192             h = h * 7 + *sp++;
193           h &= 255;
194           schematahash[h] = i;
195         }
196       data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
197       data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
198     }
199
200   for (sp = schema, len = 0, h = 0; *sp; len++)
201     h = h * 7 + *sp++;
202   h &= 255;
203   len++;
204
205   cid = schematahash[h];
206   if (cid)
207     {
208       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
209         return cid;
210       /* cache conflict, do a slow search */
211       for (cid = 1; cid < data->nschemata; cid++)
212         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
213           return cid;
214     }
215   /* a new one */
216   if (!create)
217     return 0;
218   data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
219   data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
220   /* add schema */
221   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
222   data->schemata[data->nschemata] = data->schemadatalen;
223   data->schemadatalen += len;
224   schematahash[h] = data->nschemata;
225 #if 0
226 fprintf(stderr, "schema2id: new schema\n");
227 #endif
228   return data->nschemata++;
229 }
230
231 void
232 repodata_free_schemahash(Repodata *data)
233 {
234   data->schematahash = solv_free(data->schematahash);
235   /* shrink arrays */
236   data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
237   data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
238 }
239
240
241 /***************************************************************
242  * dir pool management
243  */
244
245 #ifndef HAVE_STRCHRNUL
246 static inline const char *strchrnul(const char *str, char x)
247 {
248   const char *p = strchr(str, x);
249   return p ? p : str + strlen(str);
250 }
251 #endif
252
253 #define DIRCACHE_SIZE 41        /* < 1k */
254
255 #ifdef DIRCACHE_SIZE
256 struct dircache {
257   Id ids[DIRCACHE_SIZE];
258   char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
259 };
260 #endif
261
262 Id
263 repodata_str2dir(Repodata *data, const char *dir, int create)
264 {
265   Id id, parent;
266 #ifdef DIRCACHE_SIZE
267   const char *dirs;
268 #endif
269   const char *dire;
270
271   parent = 0;
272   if (!*dir)
273     return 0;
274   while (*dir == '/' && dir[1] == '/')
275     dir++;
276   if (*dir == '/' && !dir[1])
277     {
278       if (data->dirpool.ndirs)
279         return 1;
280       return dirpool_add_dir(&data->dirpool, 0, 1, create);
281     }
282 #ifdef DIRCACHE_SIZE
283   dirs = dir;
284   if (data->dircache)
285     {
286       int l;
287       struct dircache *dircache = data->dircache;
288       l = strlen(dir);
289       while (l > 0)
290         {
291           if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
292             {
293               parent = dircache->ids[l];
294               dir += l;
295               if (!*dir)
296                 return parent;
297               while (*dir == '/')
298                 dir++;
299               break;
300             }
301           while (--l)
302             if (dir[l] == '/')
303               break;
304         }
305     }
306 #endif
307   while (*dir)
308     {
309       dire = strchrnul(dir, '/');
310       if (data->localpool)
311         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
312       else
313         id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
314       if (!id)
315         return 0;
316       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
317       if (!parent)
318         return 0;
319 #ifdef DIRCACHE_SIZE
320       if (!data->dircache)
321         data->dircache = solv_calloc(1, sizeof(struct dircache));
322       if (data->dircache)
323         {
324           int l = dire - dirs;
325           if (l < DIRCACHE_SIZE)
326             {
327               data->dircache->ids[l] = parent;
328               memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
329             }
330         }
331 #endif
332       if (!*dire)
333         break;
334       dir = dire + 1;
335       while (*dir == '/')
336         dir++;
337     }
338   return parent;
339 }
340
341 void
342 repodata_free_dircache(Repodata *data)
343 {
344   data->dircache = solv_free(data->dircache);
345 }
346
347 const char *
348 repodata_dir2str(Repodata *data, Id did, const char *suf)
349 {
350   Pool *pool = data->repo->pool;
351   int l = 0;
352   Id parent, comp;
353   const char *comps;
354   char *p;
355
356   if (!did)
357     return suf ? suf : "";
358   parent = did;
359   while (parent)
360     {
361       comp = dirpool_compid(&data->dirpool, parent);
362       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
363       l += strlen(comps);
364       parent = dirpool_parent(&data->dirpool, parent);
365       if (parent)
366         l++;
367     }
368   if (suf)
369     l += strlen(suf) + 1;
370   p = pool_alloctmpspace(pool, l + 1) + l;
371   *p = 0;
372   if (suf)
373     {
374       p -= strlen(suf);
375       strcpy(p, suf);
376       *--p = '/';
377     }
378   parent = did;
379   while (parent)
380     {
381       comp = dirpool_compid(&data->dirpool, parent);
382       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
383       l = strlen(comps);
384       p -= l;
385       strncpy(p, comps, l);
386       parent = dirpool_parent(&data->dirpool, parent);
387       if (parent)
388         *--p = '/';
389     }
390   return p;
391 }
392
393
394 /***************************************************************
395  * data management
396  */
397
398 static inline unsigned char *
399 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
400 {
401   Id *keyp = data->schemadata + data->schemata[schema];
402   for (; *keyp; keyp++)
403     dp = data_skip_key(data, dp, data->keys + *keyp);
404   return dp;
405 }
406
407 static unsigned char *
408 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
409 {
410   int nentries, schema;
411   switch(key->type)
412     {
413     case REPOKEY_TYPE_FIXARRAY:
414       dp = data_read_id(dp, &nentries);
415       if (!nentries)
416         return dp;
417       dp = data_read_id(dp, &schema);
418       while (nentries--)
419         dp = data_skip_schema(data, dp, schema);
420       return dp;
421     case REPOKEY_TYPE_FLEXARRAY:
422       dp = data_read_id(dp, &nentries);
423       while (nentries--)
424         {
425           dp = data_read_id(dp, &schema);
426           dp = data_skip_schema(data, dp, schema);
427         }
428       return dp;
429     default:
430       if (key->storage == KEY_STORAGE_INCORE)
431         dp = data_skip(dp, key->type);
432       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
433         {
434           dp = data_skip(dp, REPOKEY_TYPE_ID);
435           dp = data_skip(dp, REPOKEY_TYPE_ID);
436         }
437       return dp;
438     }
439 }
440
441 static unsigned char *
442 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
443 {
444   Id k;
445
446   if (!keyid)
447     return 0;
448   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
449     {
450       int i;
451       for (i = 0; (k = *keyp++) != 0; i++)
452         if (k == keyid)
453           return data->incoredata + data->mainschemaoffsets[i];
454       return 0;
455     }
456   while ((k = *keyp++) != 0)
457     {
458       if (k == keyid)
459         return dp;
460       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
461         {
462           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
463           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
464           continue;
465         }
466       if (data->keys[k].storage != KEY_STORAGE_INCORE)
467         continue;
468       dp = data_skip_key(data, dp, data->keys + k);
469     }
470   return 0;
471 }
472
473 static unsigned char *
474 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
475 {
476   unsigned char *dp;
477   if (len <= 0)
478     return 0;
479   if (off >= data->lastverticaloffset)
480     {
481       off -= data->lastverticaloffset;
482       if (off + len > data->vincorelen)
483         return 0;
484       return data->vincore + off;
485     }
486   if (off + len > key->size)
487     return 0;
488   /* we now have the offset, go into vertical */
489   off += data->verticaloffset[key - data->keys];
490   /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
491   dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
492   data->storestate++;
493   if (dp)
494     dp += off % REPOPAGE_BLOBSIZE;
495   return dp;
496 }
497
498 static inline unsigned char *
499 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
500 {
501   unsigned char *dp = *dpp;
502
503   if (!dp)
504     return 0;
505   if (key->storage == KEY_STORAGE_INCORE)
506     {
507       if (advance)
508         *dpp = data_skip_key(data, dp, key);
509       return dp;
510     }
511   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
512     {
513       Id off, len;
514       dp = data_read_id(dp, &off);
515       dp = data_read_id(dp, &len);
516       if (advance)
517         *dpp = dp;
518       return get_vertical_data(data, key, off, len);
519     }
520   return 0;
521 }
522
523 static int
524 load_repodata(Repodata *data)
525 {
526   if (data->loadcallback)
527     {
528       data->loadcallback(data);
529       if (data->state == REPODATA_AVAILABLE)
530         return 1;
531     }
532   data->state = REPODATA_ERROR;
533   return 0;
534 }
535
536 static inline int
537 maybe_load_repodata(Repodata *data, Id keyname)
538 {
539   if (keyname && !repodata_precheck_keyname(data, keyname))
540     return 0;   /* do not bother... */
541   switch(data->state)
542     {
543     case REPODATA_STUB:
544       if (keyname)
545         {
546           int i;
547           for (i = 1; i < data->nkeys; i++)
548             if (keyname == data->keys[i].name)
549               break;
550           if (i == data->nkeys)
551             return 0;
552         }
553       return load_repodata(data);
554     case REPODATA_ERROR:
555       return 0;
556     case REPODATA_AVAILABLE:
557     case REPODATA_LOADING:
558       return 1;
559     default:
560       data->state = REPODATA_ERROR;
561       return 0;
562     }
563 }
564
565 static inline unsigned char *
566 solvid2data(Repodata *data, Id solvid, Id *schemap)
567 {
568   unsigned char *dp = data->incoredata;
569   if (!dp)
570     return 0;
571   if (solvid == SOLVID_META)
572     dp += 1;    /* offset of "meta" solvable */
573   else if (solvid == SOLVID_POS)
574     {
575       Pool *pool = data->repo->pool;
576       if (data->repo != pool->pos.repo)
577         return 0;
578       if (data != data->repo->repodata + pool->pos.repodataid)
579         return 0;
580       dp += pool->pos.dp;
581       if (pool->pos.dp != 1)
582         {
583           *schemap = pool->pos.schema;
584           return dp;
585         }
586     }
587   else
588     {
589       if (solvid < data->start || solvid >= data->end)
590         return 0;
591       dp += data->incoreoffset[solvid - data->start];
592     }
593   return data_read_id(dp, schemap);
594 }
595
596 /************************************************************************
597  * data lookup
598  */
599
600 static unsigned char *
601 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
602 {
603   unsigned char *dp;
604   Id schema, *keyp, *kp;
605   Repokey *key;
606
607   if (!maybe_load_repodata(data, keyname))
608     return 0;
609   dp = solvid2data(data, solvid, &schema);
610   if (!dp)
611     return 0;
612   keyp = data->schemadata + data->schemata[schema];
613   for (kp = keyp; *kp; kp++)
614     if (data->keys[*kp].name == keyname)
615       break;
616   if (!*kp)
617     return 0;
618   *keypp = key = data->keys + *kp;
619   if (key->type == REPOKEY_TYPE_DELETED)
620     return 0;
621   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
622     return dp;  /* no need to forward... */
623   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
624     return 0;   /* get_data will not work, no need to forward */
625   dp = forward_to_key(data, *kp, keyp, dp);
626   if (!dp)
627     return 0;
628   return get_data(data, key, &dp, 0);
629 }
630
631 Id
632 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
633 {
634   Id schema, *keyp, *kp;
635   if (!maybe_load_repodata(data, keyname))
636     return 0;
637   if (!solvid2data(data, solvid, &schema))
638     return 0;
639   keyp = data->schemadata + data->schemata[schema];
640   for (kp = keyp; *kp; kp++)
641     if (data->keys[*kp].name == keyname)
642       return data->keys[*kp].type;
643   return 0;
644 }
645
646 Id
647 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
648 {
649   unsigned char *dp;
650   Repokey *key;
651   Id id;
652
653   dp = find_key_data(data, solvid, keyname, &key);
654   if (!dp)
655     return 0;
656   if (key->type == REPOKEY_TYPE_CONSTANTID)
657     return key->size;
658   if (key->type != REPOKEY_TYPE_ID)
659     return 0;
660   dp = data_read_id(dp, &id);
661   return id;
662 }
663
664 const char *
665 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
666 {
667   unsigned char *dp;
668   Repokey *key;
669   Id id;
670
671   dp = find_key_data(data, solvid, keyname, &key);
672   if (!dp)
673     return 0;
674   if (key->type == REPOKEY_TYPE_STR)
675     return (const char *)dp;
676   if (key->type == REPOKEY_TYPE_CONSTANTID)
677     id = key->size;
678   else if (key->type == REPOKEY_TYPE_ID)
679     dp = data_read_id(dp, &id);
680   else
681     return 0;
682   if (data->localpool)
683     return stringpool_id2str(&data->spool, id);
684   return pool_id2str(data->repo->pool, id);
685 }
686
687 int
688 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
689 {
690   unsigned char *dp;
691   Repokey *key;
692   unsigned int high, low;
693
694   *value = 0;
695   dp = find_key_data(data, solvid, keyname, &key);
696   if (!dp)
697     return 0;
698   switch (key->type)
699     {
700     case REPOKEY_TYPE_NUM:
701       data_read_num64(dp, &low, &high);
702       *value = (unsigned long long)high << 32 | low;
703       return 1;
704     case REPOKEY_TYPE_U32:
705       data_read_u32(dp, &low);
706       *value = low;
707       return 1;
708     case REPOKEY_TYPE_CONSTANT:
709       *value = key->size;
710       return 1;
711     default:
712       return 0;
713     }
714 }
715
716 int
717 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
718 {
719   Id schema;
720   Id *keyp;
721   unsigned char *dp;
722
723   if (!maybe_load_repodata(data, keyname))
724     return 0;
725   dp = solvid2data(data, solvid, &schema);
726   if (!dp)
727     return 0;
728   /* can't use find_key_data as we need to test the type */
729   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
730     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
731       return 1;
732   return 0;
733 }
734
735 const unsigned char *
736 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
737 {
738   unsigned char *dp;
739   Repokey *key;
740
741   dp = find_key_data(data, solvid, keyname, &key);
742   if (!dp)
743     return 0;
744   switch (key->type)
745     {
746     case REPOKEY_TYPE_MD5:
747     case REPOKEY_TYPE_SHA1:
748     case REPOKEY_TYPE_SHA224:
749     case REPOKEY_TYPE_SHA256:
750     case REPOKEY_TYPE_SHA384:
751     case REPOKEY_TYPE_SHA512:
752       break;
753     default:
754       return 0;
755     }
756   *typep = key->type;
757   return dp;
758 }
759
760 int
761 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
762 {
763   unsigned char *dp;
764   Repokey *key;
765   Id id;
766   int eof = 0;
767
768   queue_empty(q);
769   dp = find_key_data(data, solvid, keyname, &key);
770   if (!dp)
771     return 0;
772   if (key->type != REPOKEY_TYPE_IDARRAY)
773     return 0;
774   for (;;)
775     {
776       dp = data_read_ideof(dp, &id, &eof);
777       queue_push(q, id);
778       if (eof)
779         break;
780     }
781   return 1;
782 }
783
784 const void *
785 repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
786 {
787   unsigned char *dp;
788   Repokey *key;
789   Id len;
790
791   dp = find_key_data(data, solvid, keyname, &key);
792   if (!dp || key->type != REPOKEY_TYPE_BINARY)
793     {
794       *lenp = 0;
795       return 0;
796     }
797   dp = data_read_id(dp, &len);
798   *lenp = len;
799   return dp;
800 }
801
802 Id
803 repodata_globalize_id(Repodata *data, Id id, int create)
804 {
805   if (!id || !data || !data->localpool)
806     return id;
807   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
808 }
809
810 Id
811 repodata_localize_id(Repodata *data, Id id, int create)
812 {
813   if (!id || !data || !data->localpool)
814     return id;
815   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
816 }
817
818 Id
819 repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
820 {
821   if (!id || !data || !fromdata)
822     return id;
823   if (!data->localpool || !fromdata->localpool)
824     {
825       if (fromdata->localpool)
826         id = repodata_globalize_id(fromdata, id, create);
827       if (data->localpool)
828         id = repodata_localize_id(data, id, create);
829       return id;
830     }
831   /* localpool is set in both data and fromdata */
832   return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
833 }
834
835 Id
836 repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
837 {
838   Id *ap;
839   if (!data->attrs)
840     return 0;
841   ap = data->attrs[solvid - data->start];
842   if (!ap)
843     return 0;
844   for (; *ap; ap += 2)
845     {
846       if (data->keys[*ap].name != keyname)
847         continue;
848       if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
849         return voidid;
850       if (data->keys[*ap].type == REPOKEY_TYPE_ID)
851         return ap[1];
852       return 0;
853     }
854   return 0;
855 }
856
857
858 /************************************************************************
859  * data search
860  */
861
862
863 int
864 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
865 {
866   switch (key->type)
867     {
868     case REPOKEY_TYPE_ID:
869     case REPOKEY_TYPE_CONSTANTID:
870     case REPOKEY_TYPE_IDARRAY:
871       if (data && data->localpool)
872         kv->str = stringpool_id2str(&data->spool, kv->id);
873       else
874         kv->str = pool_id2str(pool, kv->id);
875       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
876         {
877           const char *s;
878           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
879             ;
880           if (*s == ':' && s > kv->str)
881             kv->str = s + 1;
882         }
883       return 1;
884     case REPOKEY_TYPE_STR:
885       return 1;
886     case REPOKEY_TYPE_DIRSTRARRAY:
887       if (!(flags & SEARCH_FILES))
888         return 1;       /* match just the basename */
889       if (kv->num)
890         return 1;       /* already stringified */
891       /* Put the full filename into kv->str.  */
892       kv->str = repodata_dir2str(data, kv->id, kv->str);
893       kv->num = 1;      /* mark stringification */
894       return 1;
895     case REPOKEY_TYPE_MD5:
896     case REPOKEY_TYPE_SHA1:
897     case REPOKEY_TYPE_SHA224:
898     case REPOKEY_TYPE_SHA256:
899     case REPOKEY_TYPE_SHA384:
900     case REPOKEY_TYPE_SHA512:
901       if (!(flags & SEARCH_CHECKSUMS))
902         return 0;       /* skip em */
903       if (kv->num)
904         return 1;       /* already stringified */
905       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
906       kv->num = 1;      /* mark stringification */
907       return 1;
908     default:
909       return 0;
910     }
911 }
912
913
914 struct subschema_data {
915   Solvable *s;
916   void *cbdata;
917   KeyValue *parent;
918 };
919
920 /* search a specific repodata */
921 void
922 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
923 {
924   Id schema;
925   Repokey *key;
926   Id keyid, *kp, *keyp;
927   unsigned char *dp, *ddp;
928   int onekey = 0;
929   int stop;
930   KeyValue kv;
931   Solvable *s;
932
933   if (!maybe_load_repodata(data, keyname))
934     return;
935   if (solvid == SOLVID_SUBSCHEMA)
936     {
937       struct subschema_data *subd = cbdata;
938       cbdata = subd->cbdata;
939       s = subd->s;
940       schema = subd->parent->id;
941       dp = (unsigned char *)subd->parent->str;
942       kv.parent = subd->parent;
943     }
944   else
945     {
946       schema = 0;
947       dp = solvid2data(data, solvid, &schema);
948       if (!dp)
949         return;
950       s = data->repo->pool->solvables + solvid;
951       kv.parent = 0;
952     }
953   keyp = data->schemadata + data->schemata[schema];
954   if (keyname)
955     {
956       /* search for a specific key */
957       for (kp = keyp; *kp; kp++)
958         if (data->keys[*kp].name == keyname)
959           break;
960       if (!*kp)
961         return;
962       dp = forward_to_key(data, *kp, keyp, dp);
963       if (!dp)
964         return;
965       keyp = kp;
966       onekey = 1;
967     }
968   while ((keyid = *keyp++) != 0)
969     {
970       stop = 0;
971       key = data->keys + keyid;
972       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
973
974       if (key->type == REPOKEY_TYPE_DELETED)
975         continue;
976       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
977         {
978           struct subschema_data subd;
979           int nentries;
980           Id schema = 0;
981
982           subd.cbdata = cbdata;
983           subd.s = s;
984           subd.parent = &kv;
985           ddp = data_read_id(ddp, &nentries);
986           kv.num = nentries;
987           kv.entry = 0;
988           kv.eof = 0;
989           while (ddp && nentries > 0)
990             {
991               if (!--nentries)
992                 kv.eof = 1;
993               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
994                 ddp = data_read_id(ddp, &schema);
995               kv.id = schema;
996               kv.str = (char *)ddp;
997               stop = callback(cbdata, s, data, key, &kv);
998               if (stop > SEARCH_NEXT_KEY)
999                 return;
1000               if (stop && stop != SEARCH_ENTERSUB)
1001                 break;
1002               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
1003                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
1004               ddp = data_skip_schema(data, ddp, schema);
1005               kv.entry++;
1006             }
1007           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
1008             {
1009               /* sentinel */
1010               kv.eof = 2;
1011               kv.str = (char *)ddp;
1012               stop = callback(cbdata, s, data, key, &kv);
1013               if (stop > SEARCH_NEXT_KEY)
1014                 return;
1015             }
1016           if (onekey)
1017             return;
1018           continue;
1019         }
1020       kv.entry = 0;
1021       do
1022         {
1023           ddp = data_fetch(ddp, &kv, key);
1024           if (!ddp)
1025             break;
1026           stop = callback(cbdata, s, data, key, &kv);
1027           kv.entry++;
1028         }
1029       while (!kv.eof && !stop);
1030       if (onekey || stop > SEARCH_NEXT_KEY)
1031         return;
1032     }
1033 }
1034
1035 void
1036 repodata_setpos_kv(Repodata *data, KeyValue *kv)
1037 {
1038   Pool *pool = data->repo->pool;
1039   if (!kv)
1040     pool_clear_pos(pool);
1041   else
1042     {
1043       pool->pos.repo = data->repo;
1044       pool->pos.repodataid = data - data->repo->repodata;
1045       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
1046       pool->pos.schema = kv->id;
1047     }
1048 }
1049
1050 /************************************************************************
1051  * data iterator functions
1052  */
1053
1054 static inline Id *
1055 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1056 {
1057   kv->id = keyname;
1058   switch (keyname)
1059     {
1060     case SOLVABLE_NAME:
1061       kv->eof = 1;
1062       return &s->name;
1063     case SOLVABLE_ARCH:
1064       kv->eof = 1;
1065       return &s->arch;
1066     case SOLVABLE_EVR:
1067       kv->eof = 1;
1068       return &s->evr;
1069     case SOLVABLE_VENDOR:
1070       kv->eof = 1;
1071       return &s->vendor;
1072     case SOLVABLE_PROVIDES:
1073       kv->eof = 0;
1074       return s->provides ? s->repo->idarraydata + s->provides : 0;
1075     case SOLVABLE_OBSOLETES:
1076       kv->eof = 0;
1077       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1078     case SOLVABLE_CONFLICTS:
1079       kv->eof = 0;
1080       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1081     case SOLVABLE_REQUIRES:
1082       kv->eof = 0;
1083       return s->requires ? s->repo->idarraydata + s->requires : 0;
1084     case SOLVABLE_RECOMMENDS:
1085       kv->eof = 0;
1086       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1087     case SOLVABLE_SUPPLEMENTS:
1088       kv->eof = 0;
1089       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1090     case SOLVABLE_SUGGESTS:
1091       kv->eof = 0;
1092       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1093     case SOLVABLE_ENHANCES:
1094       kv->eof = 0;
1095       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1096     case RPM_RPMDBID:
1097       kv->eof = 1;
1098       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1099     default:
1100       return 0;
1101     }
1102 }
1103
1104 int
1105 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1106 {
1107   match = match ? solv_strdup(match) : 0;
1108   ma->match = match;
1109   ma->flags = flags;
1110   ma->error = 0;
1111   ma->matchdata = 0;
1112   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1113     {
1114       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1115       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1116       if (ma->error)
1117         {
1118           solv_free(ma->matchdata);
1119           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1120         }
1121     }
1122   if ((flags & SEARCH_FILES) != 0 && match)
1123     {
1124       /* prepare basename check */
1125       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1126         {
1127           const char *p = strrchr(match, '/');
1128           ma->matchdata = (void *)(p ? p + 1 : match);
1129         }
1130       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1131         {
1132           const char *p;
1133           for (p = match + strlen(match) - 1; p >= match; p--)
1134             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1135               break;
1136           ma->matchdata = (void *)(p + 1);
1137         }
1138     }
1139   return ma->error;
1140 }
1141
1142 void
1143 datamatcher_free(Datamatcher *ma)
1144 {
1145   if (ma->match)
1146     ma->match = solv_free((char *)ma->match);
1147   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1148     {
1149       regfree(ma->matchdata);
1150       solv_free(ma->matchdata);
1151     }
1152   ma->matchdata = 0;
1153 }
1154
1155 int
1156 datamatcher_match(Datamatcher *ma, const char *str)
1157 {
1158   int l;
1159   switch ((ma->flags & SEARCH_STRINGMASK))
1160     {
1161     case SEARCH_SUBSTRING:
1162       if (ma->flags & SEARCH_NOCASE)
1163         return strcasestr(str, ma->match) != 0;
1164       else
1165         return strstr(str, ma->match) != 0;
1166     case SEARCH_STRING:
1167       if (ma->flags & SEARCH_NOCASE)
1168         return !strcasecmp(ma->match, str);
1169       else
1170         return !strcmp(ma->match, str);
1171     case SEARCH_STRINGSTART:
1172       if (ma->flags & SEARCH_NOCASE)
1173         return !strncasecmp(ma->match, str, strlen(ma->match));
1174       else
1175         return !strncmp(ma->match, str, strlen(ma->match));
1176     case SEARCH_STRINGEND:
1177       l = strlen(str) - strlen(ma->match);
1178       if (l < 0)
1179         return 0;
1180       if (ma->flags & SEARCH_NOCASE)
1181         return !strcasecmp(ma->match, str + l);
1182       else
1183         return !strcmp(ma->match, str + l);
1184     case SEARCH_GLOB:
1185       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1186     case SEARCH_REGEX:
1187       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1188     default:
1189       return 0;
1190     }
1191 }
1192
1193 /* check if the matcher can match the provides basename */
1194
1195 int
1196 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1197 {
1198   int l;
1199   const char *match = ma->matchdata;
1200   if (!match)
1201     return 1;
1202   switch (ma->flags & SEARCH_STRINGMASK)
1203     {
1204     case SEARCH_STRING:
1205       break;
1206     case SEARCH_STRINGEND:
1207       if (match != ma->match)
1208         break;          /* had slash, do exact match on basename */
1209       /* FALLTHROUGH */
1210     case SEARCH_GLOB:
1211       /* check if the basename ends with match */
1212       l = strlen(basename) - strlen(match);
1213       if (l < 0)
1214         return 0;
1215       basename += l;
1216       break;
1217     default:
1218       return 1; /* maybe matches */
1219     }
1220   if ((ma->flags & SEARCH_NOCASE) != 0)
1221     return !strcasecmp(match, basename);
1222   else
1223     return !strcmp(match, basename);
1224 }
1225
1226 int
1227 repodata_filelistfilter_matches(Repodata *data, const char *str)
1228 {
1229   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1230   /* for now hardcoded */
1231   if (strstr(str, "bin/"))
1232     return 1;
1233   if (!strncmp(str, "/etc/", 5))
1234     return 1;
1235   if (!strcmp(str, "/usr/lib/sendmail"))
1236     return 1;
1237   return 0;
1238 }
1239
1240
1241 enum {
1242   di_bye,
1243
1244   di_enterrepo,
1245   di_entersolvable,
1246   di_enterrepodata,
1247   di_enterschema,
1248   di_enterkey,
1249
1250   di_nextattr,
1251   di_nextkey,
1252   di_nextrepodata,
1253   di_nextsolvable,
1254   di_nextrepo,
1255
1256   di_enterarray,
1257   di_nextarrayelement,
1258
1259   di_entersub,
1260   di_leavesub,
1261
1262   di_nextsolvablekey,
1263   di_entersolvablekey,
1264   di_nextsolvableattr
1265 };
1266
1267 /* see dataiterator.h for documentation */
1268 int
1269 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1270 {
1271   memset(di, 0, sizeof(*di));
1272   di->pool = pool;
1273   di->flags = flags & ~SEARCH_THISSOLVID;
1274   if (!pool || (repo && repo->pool != pool))
1275     {
1276       di->state = di_bye;
1277       return -1;
1278     }
1279   if (match)
1280     {
1281       int error;
1282       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1283         {
1284           di->state = di_bye;
1285           return error;
1286         }
1287     }
1288   di->keyname = keyname;
1289   di->keynames[0] = keyname;
1290   dataiterator_set_search(di, repo, p);
1291   return 0;
1292 }
1293
1294 void
1295 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1296 {
1297   *di = *from;
1298   if (di->dupstr)
1299     {
1300       if (di->dupstr == di->kv.str)
1301         di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
1302       else
1303         {
1304           di->dupstr = 0;
1305           di->dupstrn = 0;
1306         }
1307     }
1308   memset(&di->matcher, 0, sizeof(di->matcher));
1309   if (from->matcher.match)
1310     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1311   if (di->nparents)
1312     {
1313       /* fix pointers */
1314       int i;
1315       for (i = 1; i < di->nparents; i++)
1316         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1317       di->kv.parent = &di->parents[di->nparents - 1].kv;
1318     }
1319 }
1320
1321 int
1322 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1323 {
1324   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1325   datamatcher_free(&di->matcher);
1326   memset(&di->matcher, 0, sizeof(di->matcher));
1327   if (match)
1328     {
1329       int error;
1330       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1331         {
1332           di->state = di_bye;
1333           return error;
1334         }
1335     }
1336   return 0;
1337 }
1338
1339 void
1340 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1341 {
1342   di->repo = repo;
1343   di->repoid = 0;
1344   di->flags &= ~SEARCH_THISSOLVID;
1345   di->nparents = 0;
1346   di->rootlevel = 0;
1347   di->repodataid = 1;
1348   if (!di->pool->urepos)
1349     {
1350       di->state = di_bye;
1351       return;
1352     }
1353   if (!repo)
1354     {
1355       di->repoid = 1;
1356       di->repo = di->pool->repos[di->repoid];
1357     }
1358   di->state = di_enterrepo;
1359   if (p)
1360     dataiterator_jump_to_solvid(di, p);
1361 }
1362
1363 void
1364 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1365 {
1366   di->nkeynames = 0;
1367   di->keyname = keyname;
1368   di->keynames[0] = keyname;
1369 }
1370
1371 void
1372 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1373 {
1374   int i;
1375
1376   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1377     {
1378       di->state = di_bye;       /* sorry */
1379       return;
1380     }
1381   for (i = di->nkeynames + 1; i > 0; i--)
1382     di->keynames[i] = di->keynames[i - 1];
1383   di->keynames[0] = di->keyname = keyname;
1384   di->nkeynames++;
1385 }
1386
1387 void
1388 dataiterator_free(Dataiterator *di)
1389 {
1390   if (di->matcher.match)
1391     datamatcher_free(&di->matcher);
1392   if (di->dupstr)
1393     solv_free(di->dupstr);
1394 }
1395
1396 static unsigned char *
1397 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1398 {
1399   Id *keyp;
1400   Repokey *keys = di->data->keys, *key;
1401   unsigned char *dp;
1402
1403   for (keyp = di->keyp; *keyp; keyp++)
1404     if (keys[*keyp].name == keyname)
1405       break;
1406   if (!*keyp)
1407     return 0;
1408   key = keys + *keyp;
1409   if (key->type == REPOKEY_TYPE_DELETED)
1410     return 0;
1411   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
1412     return 0;           /* get_data will not work, no need to forward */
1413   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1414   if (!dp)
1415     return 0;
1416   di->keyp = keyp;
1417   return dp;
1418 }
1419
1420 static inline int
1421 is_filelist_extension(Repodata *data)
1422 {
1423   int j;
1424   if (!repodata_precheck_keyname(data, SOLVABLE_FILELIST))
1425     return 0;
1426   for (j = 1; j < data->nkeys; j++)
1427     if (data->keys[j].name == SOLVABLE_FILELIST)
1428       break;
1429   if (j == data->nkeys)
1430     return 0;
1431   if (data->state != REPODATA_AVAILABLE)
1432     return 1;
1433   for (j = 1; j < data->nkeys; j++)
1434     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1435       return 0;
1436   return 1;
1437 }
1438
1439 static int
1440 dataiterator_filelistcheck(Dataiterator *di)
1441 {
1442   int j;
1443   int needcomplete = 0;
1444   Repodata *data = di->data;
1445
1446   if ((di->flags & SEARCH_COMPLETE_FILELIST) != 0)
1447     if (!di->matcher.match
1448        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1449            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1450        || !repodata_filelistfilter_matches(data, di->matcher.match))
1451       needcomplete = 1;
1452   if (data->state != REPODATA_AVAILABLE)
1453     return needcomplete ? 1 : 0;
1454   if (!needcomplete)
1455     {
1456       /* we don't need the complete filelist, so ignore all stubs */
1457       for (j = 1; j < data->nkeys; j++)
1458         if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1459           return 1;
1460       return 0;
1461     }
1462   else
1463     {
1464       /* we need the complete filelist. check if we habe a filtered filelist and there's
1465        * a extension with the complete filelist later on */
1466       for (j = 1; j < data->nkeys; j++)
1467         if (data->keys[j].name == SOLVABLE_FILELIST)
1468           break;
1469       if (j == data->nkeys)
1470         return 0;       /* does not have filelist */
1471       for (j = 1; j < data->nkeys; j++)
1472         if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1473           break;
1474       if (j == data->nkeys)
1475         return 1;       /* this is the externsion */
1476       while (data - data->repo->repodata + 1 < data->repo->nrepodata)
1477         {
1478           data++;
1479           if (is_filelist_extension(data))
1480             return 0;
1481         }
1482       return 1;
1483     }
1484 }
1485
1486 int
1487 dataiterator_step(Dataiterator *di)
1488 {
1489   Id schema;
1490
1491   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
1492     unsigned int ddpoff = di->ddp - di->vert_ddp;
1493     di->vert_off += ddpoff;
1494     di->vert_len -= ddpoff;
1495     di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1496     di->vert_storestate = di->data->storestate;
1497     if (!di->ddp)
1498       di->state = di_nextkey;
1499   }
1500   for (;;)
1501     {
1502       switch (di->state)
1503         {
1504         case di_enterrepo: di_enterrepo:
1505           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1506             goto di_nextrepo;
1507           if (!(di->flags & SEARCH_THISSOLVID))
1508             {
1509               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1510               goto di_nextsolvable;
1511             }
1512           /* FALLTHROUGH */
1513
1514         case di_entersolvable: di_entersolvable:
1515           if (di->repodataid)
1516             {
1517               di->repodataid = 1;       /* reset repodata iterator */
1518               if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
1519                 {
1520                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1521
1522                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1523                   di->data = 0;
1524                   goto di_entersolvablekey;
1525                 }
1526             }
1527           /* FALLTHROUGH */
1528
1529         case di_enterrepodata: di_enterrepodata:
1530           if (di->repodataid)
1531             {
1532               if (di->repodataid >= di->repo->nrepodata)
1533                 goto di_nextsolvable;
1534               di->data = di->repo->repodata + di->repodataid;
1535             }
1536           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1537             goto di_nextrepodata;
1538           if (!maybe_load_repodata(di->data, di->keyname))
1539             goto di_nextrepodata;
1540           di->dp = solvid2data(di->data, di->solvid, &schema);
1541           if (!di->dp)
1542             goto di_nextrepodata;
1543           if (di->solvid == SOLVID_POS)
1544             di->solvid = di->pool->pos.solvid;
1545           /* reset key iterator */
1546           di->keyp = di->data->schemadata + di->data->schemata[schema];
1547           /* FALLTHROUGH */
1548
1549         case di_enterschema: di_enterschema:
1550           if (di->keyname)
1551             di->dp = dataiterator_find_keyname(di, di->keyname);
1552           if (!di->dp || !*di->keyp)
1553             {
1554               if (di->kv.parent)
1555                 goto di_leavesub;
1556               goto di_nextrepodata;
1557             }
1558           /* FALLTHROUGH */
1559
1560         case di_enterkey: di_enterkey:
1561           di->kv.entry = -1;
1562           di->key = di->data->keys + *di->keyp;
1563           if (!di->dp)
1564             goto di_nextkey;
1565           /* this is get_data() modified to store vert_ data */
1566           if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1567             {
1568               Id off, len;
1569               di->dp = data_read_id(di->dp, &off);
1570               di->dp = data_read_id(di->dp, &len);
1571               di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1572               di->vert_off = off;
1573               di->vert_len = len;
1574               di->vert_storestate = di->data->storestate;
1575             }
1576           else if (di->key->storage == KEY_STORAGE_INCORE)
1577             {
1578               di->ddp = di->dp;
1579               if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1580                 di->dp = data_skip_key(di->data, di->dp, di->key);
1581             }
1582           else
1583             di->ddp = 0;
1584           if (!di->ddp)
1585             goto di_nextkey;
1586           if (di->key->type == REPOKEY_TYPE_DELETED)
1587             goto di_nextkey;
1588           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1589             goto di_enterarray;
1590           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1591             goto di_nextkey;
1592           /* FALLTHROUGH */
1593
1594         case di_nextattr:
1595           di->kv.entry++;
1596           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1597           if (di->kv.eof)
1598             di->state = di_nextkey;
1599           else
1600             di->state = di_nextattr;
1601           break;
1602
1603         case di_nextkey: di_nextkey:
1604           if (!di->keyname && *++di->keyp)
1605             goto di_enterkey;
1606           if (di->kv.parent)
1607             goto di_leavesub;
1608           /* FALLTHROUGH */
1609
1610         case di_nextrepodata: di_nextrepodata:
1611           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1612               goto di_enterrepodata;
1613           /* FALLTHROUGH */
1614
1615         case di_nextsolvable: di_nextsolvable:
1616           if (!(di->flags & SEARCH_THISSOLVID))
1617             {
1618               if (di->solvid < 0)
1619                 di->solvid = di->repo->start;
1620               else
1621                 di->solvid++;
1622               for (; di->solvid < di->repo->end; di->solvid++)
1623                 {
1624                   if (di->pool->solvables[di->solvid].repo == di->repo)
1625                     goto di_entersolvable;
1626                 }
1627             }
1628           /* FALLTHROUGH */
1629
1630         case di_nextrepo: di_nextrepo:
1631           if (di->repoid > 0)
1632             {
1633               di->repoid++;
1634               di->repodataid = 1;
1635               if (di->repoid < di->pool->nrepos)
1636                 {
1637                   di->repo = di->pool->repos[di->repoid];
1638                   goto di_enterrepo;
1639                 }
1640             }
1641         /* FALLTHROUGH */
1642
1643         case di_bye: di_bye:
1644           di->state = di_bye;
1645           return 0;
1646
1647         case di_enterarray: di_enterarray:
1648           if (di->key->name == REPOSITORY_SOLVABLES)
1649             goto di_nextkey;
1650           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1651           di->kv.eof = 0;
1652           di->kv.entry = -1;
1653           /* FALLTHROUGH */
1654
1655         case di_nextarrayelement: di_nextarrayelement:
1656           di->kv.entry++;
1657           if (di->kv.entry)
1658             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1659           if (di->kv.entry == di->kv.num)
1660             {
1661               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1662                 goto di_nextkey;
1663               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1664                 goto di_nextkey;
1665               di->kv.str = (char *)di->ddp;
1666               di->kv.eof = 2;
1667               di->state = di_nextkey;
1668               break;
1669             }
1670           if (di->kv.entry == di->kv.num - 1)
1671             di->kv.eof = 1;
1672           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1673             di->ddp = data_read_id(di->ddp, &di->kv.id);
1674           di->kv.str = (char *)di->ddp;
1675           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1676             goto di_entersub;
1677           if ((di->flags & SEARCH_SUB) != 0)
1678             di->state = di_entersub;
1679           else
1680             di->state = di_nextarrayelement;
1681           break;
1682
1683         case di_entersub: di_entersub:
1684           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1685             goto di_nextarrayelement;   /* sorry, full */
1686           di->parents[di->nparents].kv = di->kv;
1687           di->parents[di->nparents].dp = di->dp;
1688           di->parents[di->nparents].keyp = di->keyp;
1689           di->dp = (unsigned char *)di->kv.str;
1690           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1691           memset(&di->kv, 0, sizeof(di->kv));
1692           di->kv.parent = &di->parents[di->nparents].kv;
1693           di->nparents++;
1694           di->keyname = di->keynames[di->nparents - di->rootlevel];
1695           goto di_enterschema;
1696
1697         case di_leavesub: di_leavesub:
1698           if (di->nparents - 1 < di->rootlevel)
1699             goto di_bye;
1700           di->nparents--;
1701           di->dp = di->parents[di->nparents].dp;
1702           di->kv = di->parents[di->nparents].kv;
1703           di->keyp = di->parents[di->nparents].keyp;
1704           di->key = di->data->keys + *di->keyp;
1705           di->ddp = (unsigned char *)di->kv.str;
1706           di->keyname = di->keynames[di->nparents - di->rootlevel];
1707           goto di_nextarrayelement;
1708
1709         /* special solvable attr handling follows */
1710
1711         case di_nextsolvablekey: di_nextsolvablekey:
1712           if (di->keyname || di->key->name == RPM_RPMDBID)
1713             goto di_enterrepodata;
1714           di->key++;
1715           /* FALLTHROUGH */
1716
1717         case di_entersolvablekey: di_entersolvablekey:
1718           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1719           if (!di->idp || !*di->idp)
1720             goto di_nextsolvablekey;
1721           if (di->kv.eof)
1722             {
1723               /* not an array */
1724               di->kv.id = *di->idp;
1725               di->kv.num = *di->idp;    /* for rpmdbid */
1726               di->kv.num2 = 0;          /* for rpmdbid */
1727               di->kv.entry = 0;
1728               di->state = di_nextsolvablekey;
1729               break;
1730             }
1731           di->kv.entry = -1;
1732           /* FALLTHROUGH */
1733
1734         case di_nextsolvableattr:
1735           di->state = di_nextsolvableattr;
1736           di->kv.id = *di->idp++;
1737           di->kv.entry++;
1738           if (!*di->idp)
1739             {
1740               di->kv.eof = 1;
1741               di->state = di_nextsolvablekey;
1742             }
1743           break;
1744
1745         }
1746
1747       if (di->matcher.match)
1748         {
1749           /* simple pre-check so that we don't need to stringify */
1750           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1751             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1752               continue;
1753           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1754             {
1755               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1756                 return 1;
1757               continue;
1758             }
1759           if (!datamatcher_match(&di->matcher, di->kv.str))
1760             continue;
1761         }
1762       else
1763         {
1764           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1765             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1766         }
1767       /* found something! */
1768       return 1;
1769     }
1770 }
1771
1772 void
1773 dataiterator_entersub(Dataiterator *di)
1774 {
1775   if (di->state == di_nextarrayelement)
1776     di->state = di_entersub;
1777 }
1778
1779 void
1780 dataiterator_setpos(Dataiterator *di)
1781 {
1782   if (di->kv.eof == 2)
1783     {
1784       pool_clear_pos(di->pool);
1785       return;
1786     }
1787   di->pool->pos.solvid = di->solvid;
1788   di->pool->pos.repo = di->repo;
1789   di->pool->pos.repodataid = di->data - di->repo->repodata;
1790   di->pool->pos.schema = di->kv.id;
1791   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1792 }
1793
1794 void
1795 dataiterator_setpos_parent(Dataiterator *di)
1796 {
1797   if (!di->kv.parent || di->kv.parent->eof == 2)
1798     {
1799       pool_clear_pos(di->pool);
1800       return;
1801     }
1802   di->pool->pos.solvid = di->solvid;
1803   di->pool->pos.repo = di->repo;
1804   di->pool->pos.repodataid = di->data - di->repo->repodata;
1805   di->pool->pos.schema = di->kv.parent->id;
1806   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1807 }
1808
1809 /* clones just the position, not the search keys/matcher */
1810 void
1811 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1812 {
1813   di->state = from->state;
1814   di->flags &= ~SEARCH_THISSOLVID;
1815   di->flags |= (from->flags & SEARCH_THISSOLVID);
1816   di->repo = from->repo;
1817   di->data = from->data;
1818   di->dp = from->dp;
1819   di->ddp = from->ddp;
1820   di->idp = from->idp;
1821   di->keyp = from->keyp;
1822   di->key = from->key;
1823   di->kv = from->kv;
1824   di->repodataid = from->repodataid;
1825   di->solvid = from->solvid;
1826   di->repoid = from->repoid;
1827   di->rootlevel = from->rootlevel;
1828   memcpy(di->parents, from->parents, sizeof(from->parents));
1829   di->nparents = from->nparents;
1830   if (di->nparents)
1831     {
1832       int i;
1833       for (i = 1; i < di->nparents; i++)
1834         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1835       di->kv.parent = &di->parents[di->nparents - 1].kv;
1836     }
1837   di->dupstr = 0;
1838   di->dupstrn = 0;
1839   if (from->dupstr && from->dupstr == from->kv.str)
1840     {
1841       di->dupstrn = from->dupstrn;
1842       di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
1843     }
1844 }
1845
1846 void
1847 dataiterator_seek(Dataiterator *di, int whence)
1848 {
1849   if ((whence & DI_SEEK_STAY) != 0)
1850     di->rootlevel = di->nparents;
1851   switch (whence & ~DI_SEEK_STAY)
1852     {
1853     case DI_SEEK_CHILD:
1854       if (di->state != di_nextarrayelement)
1855         break;
1856       if ((whence & DI_SEEK_STAY) != 0)
1857         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1858       di->state = di_entersub;
1859       break;
1860     case DI_SEEK_PARENT:
1861       if (!di->nparents)
1862         {
1863           di->state = di_bye;
1864           break;
1865         }
1866       di->nparents--;
1867       if (di->rootlevel > di->nparents)
1868         di->rootlevel = di->nparents;
1869       di->dp = di->parents[di->nparents].dp;
1870       di->kv = di->parents[di->nparents].kv;
1871       di->keyp = di->parents[di->nparents].keyp;
1872       di->key = di->data->keys + *di->keyp;
1873       di->ddp = (unsigned char *)di->kv.str;
1874       di->keyname = di->keynames[di->nparents - di->rootlevel];
1875       di->state = di_nextarrayelement;
1876       break;
1877     case DI_SEEK_REWIND:
1878       if (!di->nparents)
1879         {
1880           di->state = di_bye;
1881           break;
1882         }
1883       di->dp = (unsigned char *)di->kv.parent->str;
1884       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1885       di->state = di_enterschema;
1886       break;
1887     default:
1888       break;
1889     }
1890 }
1891
1892 void
1893 dataiterator_skip_attribute(Dataiterator *di)
1894 {
1895   if (di->state == di_nextsolvableattr)
1896     di->state = di_nextsolvablekey;
1897   else
1898     di->state = di_nextkey;
1899 }
1900
1901 void
1902 dataiterator_skip_solvable(Dataiterator *di)
1903 {
1904   di->nparents = 0;
1905   di->kv.parent = 0;
1906   di->rootlevel = 0;
1907   di->keyname = di->keynames[0];
1908   di->state = di_nextsolvable;
1909 }
1910
1911 void
1912 dataiterator_skip_repo(Dataiterator *di)
1913 {
1914   di->nparents = 0;
1915   di->kv.parent = 0;
1916   di->rootlevel = 0;
1917   di->keyname = di->keynames[0];
1918   di->state = di_nextrepo;
1919 }
1920
1921 void
1922 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1923 {
1924   di->nparents = 0;
1925   di->kv.parent = 0;
1926   di->rootlevel = 0;
1927   di->keyname = di->keynames[0];
1928   if (solvid == SOLVID_POS)
1929     {
1930       di->repo = di->pool->pos.repo;
1931       if (!di->repo)
1932         {
1933           di->state = di_bye;
1934           return;
1935         }
1936       di->repoid = 0;
1937       di->data = di->repo->repodata + di->pool->pos.repodataid;
1938       di->repodataid = 0;
1939       di->solvid = solvid;
1940       di->state = di_enterrepo;
1941       di->flags |= SEARCH_THISSOLVID;
1942       return;
1943     }
1944   if (solvid > 0)
1945     {
1946       di->repo = di->pool->solvables[solvid].repo;
1947       di->repoid = 0;
1948     }
1949   else if (di->repoid > 0)
1950     {
1951       if (!di->pool->urepos)
1952         {
1953           di->state = di_bye;
1954           return;
1955         }
1956       di->repoid = 1;
1957       di->repo = di->pool->repos[di->repoid];
1958     }
1959   di->repodataid = 1;
1960   di->solvid = solvid;
1961   if (solvid)
1962     di->flags |= SEARCH_THISSOLVID;
1963   di->state = di_enterrepo;
1964 }
1965
1966 void
1967 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1968 {
1969   di->nparents = 0;
1970   di->kv.parent = 0;
1971   di->rootlevel = 0;
1972   di->repo = repo;
1973   di->repoid = 0;       /* 0 means stay at repo */
1974   di->repodataid = 1;
1975   di->solvid = 0;
1976   di->flags &= ~SEARCH_THISSOLVID;
1977   di->state = di_enterrepo;
1978 }
1979
1980 int
1981 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1982 {
1983   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1984     return 0;
1985   if (!ma)
1986     return 1;
1987   return datamatcher_match(ma, di->kv.str);
1988 }
1989
1990 void
1991 dataiterator_strdup(Dataiterator *di)
1992 {
1993   int l = -1;
1994
1995   if (!di->kv.str || di->kv.str == di->dupstr)
1996     return;
1997   switch (di->key->type)
1998     {
1999     case REPOKEY_TYPE_MD5:
2000     case REPOKEY_TYPE_SHA1:
2001     case REPOKEY_TYPE_SHA224:
2002     case REPOKEY_TYPE_SHA256:
2003     case REPOKEY_TYPE_SHA384:
2004     case REPOKEY_TYPE_SHA512:
2005     case REPOKEY_TYPE_DIRSTRARRAY:
2006       if (di->kv.num)   /* was it stringified into tmp space? */
2007         l = strlen(di->kv.str) + 1;
2008       break;
2009     default:
2010       break;
2011     }
2012   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2013     {
2014       switch (di->key->type)
2015         {
2016         case REPOKEY_TYPE_STR:
2017         case REPOKEY_TYPE_DIRSTRARRAY:
2018           l = strlen(di->kv.str) + 1;
2019           break;
2020         case REPOKEY_TYPE_MD5:
2021           l = SIZEOF_MD5;
2022           break;
2023         case REPOKEY_TYPE_SHA1:
2024           l = SIZEOF_SHA1;
2025           break;
2026         case REPOKEY_TYPE_SHA224:
2027           l = SIZEOF_SHA224;
2028           break;
2029         case REPOKEY_TYPE_SHA256:
2030           l = SIZEOF_SHA256;
2031           break;
2032         case REPOKEY_TYPE_SHA384:
2033           l = SIZEOF_SHA384;
2034           break;
2035         case REPOKEY_TYPE_SHA512:
2036           l = SIZEOF_SHA512;
2037           break;
2038         case REPOKEY_TYPE_BINARY:
2039           l = di->kv.num;
2040           break;
2041         }
2042     }
2043   if (l >= 0)
2044     {
2045       if (!di->dupstrn || di->dupstrn < l)
2046         {
2047           di->dupstrn = l + 16;
2048           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
2049         }
2050       if (l)
2051         memcpy(di->dupstr, di->kv.str, l);
2052       di->kv.str = di->dupstr;
2053     }
2054 }
2055
2056 /************************************************************************
2057  * data modify functions
2058  */
2059
2060 /* extend repodata so that it includes solvables p */
2061 void
2062 repodata_extend(Repodata *data, Id p)
2063 {
2064   if (data->start == data->end)
2065     data->start = data->end = p;
2066   if (p >= data->end)
2067     {
2068       int old = data->end - data->start;
2069       int new = p - data->end + 1;
2070       if (data->attrs)
2071         {
2072           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
2073           memset(data->attrs + old, 0, new * sizeof(Id *));
2074         }
2075       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
2076       memset(data->incoreoffset + old, 0, new * sizeof(Id));
2077       data->end = p + 1;
2078     }
2079   if (p < data->start)
2080     {
2081       int old = data->end - data->start;
2082       int new = data->start - p;
2083       if (data->attrs)
2084         {
2085           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
2086           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
2087           memset(data->attrs, 0, new * sizeof(Id *));
2088         }
2089       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
2090       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
2091       memset(data->incoreoffset, 0, new * sizeof(Id));
2092       data->start = p;
2093     }
2094 }
2095
2096 /* shrink end of repodata */
2097 void
2098 repodata_shrink(Repodata *data, int end)
2099 {
2100   int i;
2101
2102   if (data->end <= end)
2103     return;
2104   if (data->start >= end)
2105     {
2106       if (data->attrs)
2107         {
2108           for (i = 0; i < data->end - data->start; i++)
2109             solv_free(data->attrs[i]);
2110           data->attrs = solv_free(data->attrs);
2111         }
2112       data->incoreoffset = solv_free(data->incoreoffset);
2113       data->start = data->end = 0;
2114       return;
2115     }
2116   if (data->attrs)
2117     {
2118       for (i = end; i < data->end; i++)
2119         solv_free(data->attrs[i - data->start]);
2120       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2121     }
2122   if (data->incoreoffset)
2123     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2124   data->end = end;
2125 }
2126
2127 /* extend repodata so that it includes solvables from start to start + num - 1 */
2128 void
2129 repodata_extend_block(Repodata *data, Id start, Id num)
2130 {
2131   if (!num)
2132     return;
2133   if (!data->incoreoffset)
2134     {
2135       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2136       data->start = start;
2137       data->end = start + num;
2138       return;
2139     }
2140   repodata_extend(data, start);
2141   if (num > 1)
2142     repodata_extend(data, start + num - 1);
2143 }
2144
2145 /**********************************************************************/
2146
2147
2148 #define REPODATA_ATTRS_BLOCK 31
2149 #define REPODATA_ATTRDATA_BLOCK 1023
2150 #define REPODATA_ATTRIDDATA_BLOCK 63
2151 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2152
2153
2154 Id
2155 repodata_new_handle(Repodata *data)
2156 {
2157   if (!data->nxattrs)
2158     {
2159       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2160       data->nxattrs = 2;        /* -1: SOLVID_META */
2161     }
2162   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2163   data->xattrs[data->nxattrs] = 0;
2164   return -(data->nxattrs++);
2165 }
2166
2167 static inline Id **
2168 repodata_get_attrp(Repodata *data, Id handle)
2169 {
2170   if (handle < 0)
2171     {
2172       if (handle == SOLVID_META && !data->xattrs)
2173         {
2174           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2175           data->nxattrs = 2;
2176         }
2177       return data->xattrs - handle;
2178     }
2179   if (handle < data->start || handle >= data->end)
2180     repodata_extend(data, handle);
2181   if (!data->attrs)
2182     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2183   return data->attrs + (handle - data->start);
2184 }
2185
2186 static void
2187 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2188 {
2189   Id *pp;
2190   Id *ap, **app;
2191   int i;
2192
2193   app = repodata_get_attrp(data, handle);
2194   ap = *app;
2195   i = 0;
2196   if (ap)
2197     {
2198       /* Determine equality based on the name only, allows us to change
2199          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2200       for (pp = ap; *pp; pp += 2)
2201         if (data->keys[*pp].name == data->keys[keyid].name)
2202           break;
2203       if (*pp)
2204         {
2205           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2206             {
2207               pp[0] = keyid;
2208               pp[1] = val;
2209             }
2210           return;
2211         }
2212       i = pp - ap;
2213     }
2214   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2215   *app = ap;
2216   pp = ap + i;
2217   *pp++ = keyid;
2218   *pp++ = val;
2219   *pp = 0;
2220 }
2221
2222
2223 static void
2224 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2225 {
2226   Id keyid;
2227
2228   keyid = repodata_key2id(data, key, 1);
2229   repodata_insert_keyid(data, solvid, keyid, val, 1);
2230 }
2231
2232 void
2233 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2234 {
2235   Repokey key;
2236   key.name = keyname;
2237   key.type = REPOKEY_TYPE_ID;
2238   key.size = 0;
2239   key.storage = KEY_STORAGE_INCORE;
2240   repodata_set(data, solvid, &key, id);
2241 }
2242
2243 void
2244 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2245 {
2246   Repokey key;
2247   key.name = keyname;
2248   key.type = REPOKEY_TYPE_NUM;
2249   key.size = 0;
2250   key.storage = KEY_STORAGE_INCORE;
2251   if (num >= 0x80000000)
2252     {
2253       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2254       data->attrnum64data[data->attrnum64datalen] = num;
2255       num = 0x80000000 | data->attrnum64datalen++;
2256     }
2257   repodata_set(data, solvid, &key, (Id)num);
2258 }
2259
2260 void
2261 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2262 {
2263   Repokey key;
2264   Id id;
2265   if (data->localpool)
2266     id = stringpool_str2id(&data->spool, str, 1);
2267   else
2268     id = pool_str2id(data->repo->pool, str, 1);
2269   key.name = keyname;
2270   key.type = REPOKEY_TYPE_ID;
2271   key.size = 0;
2272   key.storage = KEY_STORAGE_INCORE;
2273   repodata_set(data, solvid, &key, id);
2274 }
2275
2276 void
2277 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2278 {
2279   Repokey key;
2280   key.name = keyname;
2281   key.type = REPOKEY_TYPE_CONSTANT;
2282   key.size = constant;
2283   key.storage = KEY_STORAGE_INCORE;
2284   repodata_set(data, solvid, &key, 0);
2285 }
2286
2287 void
2288 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2289 {
2290   Repokey key;
2291   key.name = keyname;
2292   key.type = REPOKEY_TYPE_CONSTANTID;
2293   key.size = id;
2294   key.storage = KEY_STORAGE_INCORE;
2295   repodata_set(data, solvid, &key, 0);
2296 }
2297
2298 void
2299 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2300 {
2301   Repokey key;
2302   key.name = keyname;
2303   key.type = REPOKEY_TYPE_VOID;
2304   key.size = 0;
2305   key.storage = KEY_STORAGE_INCORE;
2306   repodata_set(data, solvid, &key, 0);
2307 }
2308
2309 void
2310 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2311 {
2312   Repokey key;
2313   int l;
2314
2315   l = strlen(str) + 1;
2316   key.name = keyname;
2317   key.type = REPOKEY_TYPE_STR;
2318   key.size = 0;
2319   key.storage = KEY_STORAGE_INCORE;
2320   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2321   memcpy(data->attrdata + data->attrdatalen, str, l);
2322   repodata_set(data, solvid, &key, data->attrdatalen);
2323   data->attrdatalen += l;
2324 }
2325
2326 void
2327 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2328 {
2329   Repokey key;
2330   unsigned char *dp;
2331
2332   if (len < 0)
2333     return;
2334   key.name = keyname;
2335   key.type = REPOKEY_TYPE_BINARY;
2336   key.size = 0;
2337   key.storage = KEY_STORAGE_INCORE;
2338   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2339   dp = data->attrdata + data->attrdatalen;
2340   if (len >= (1 << 14))
2341     {
2342       if (len >= (1 << 28))
2343         *dp++ = (len >> 28) | 128;
2344       if (len >= (1 << 21))
2345         *dp++ = (len >> 21) | 128;
2346       *dp++ = (len >> 14) | 128;
2347     }
2348   if (len >= (1 << 7))
2349     *dp++ = (len >> 7) | 128;
2350   *dp++ = len & 127;
2351   if (len)
2352     memcpy(dp, buf, len);
2353   repodata_set(data, solvid, &key, data->attrdatalen);
2354   data->attrdatalen = dp + len - data->attrdata;
2355 }
2356
2357 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2358  * so that the caller can append entrysize new elements plus the termination zero there */
2359 static void
2360 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2361 {
2362   int oldsize;
2363   Id *ida, *pp, **ppp;
2364
2365   /* check if it is the same as last time, this speeds things up a lot */
2366   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2367     {
2368       /* great! just append the new data */
2369       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2370       data->attriddatalen--;    /* overwrite terminating 0  */
2371       data->lastdatalen += entrysize;
2372       return;
2373     }
2374
2375   ppp = repodata_get_attrp(data, handle);
2376   pp = *ppp;
2377   if (pp)
2378     {
2379       for (; *pp; pp += 2)
2380         if (data->keys[*pp].name == keyname)
2381           break;
2382     }
2383   if (!pp || !*pp || data->keys[*pp].type != keytype)
2384     {
2385       /* not found. allocate new key */
2386       Repokey key;
2387       Id keyid;
2388       key.name = keyname;
2389       key.type = keytype;
2390       key.size = 0;
2391       key.storage = KEY_STORAGE_INCORE;
2392       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2393       keyid = repodata_key2id(data, &key, 1);
2394       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2395       data->lasthandle = handle;
2396       data->lastkey = keyid;
2397       data->lastdatalen = data->attriddatalen + entrysize + 1;
2398       return;
2399     }
2400   oldsize = 0;
2401   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2402     oldsize += entrysize;
2403   if (ida + 1 == data->attriddata + data->attriddatalen)
2404     {
2405       /* this was the last entry, just append it */
2406       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2407       data->attriddatalen--;    /* overwrite terminating 0  */
2408     }
2409   else
2410     {
2411       /* too bad. move to back. */
2412       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2413       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2414       pp[1] = data->attriddatalen;
2415       data->attriddatalen += oldsize;
2416     }
2417   data->lasthandle = handle;
2418   data->lastkey = *pp;
2419   data->lastdatalen = data->attriddatalen + entrysize + 1;
2420 }
2421
2422 void
2423 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2424                       const unsigned char *str)
2425 {
2426   Repokey key;
2427   int l;
2428
2429   if (!(l = solv_chksum_len(type)))
2430     return;
2431   key.name = keyname;
2432   key.type = type;
2433   key.size = 0;
2434   key.storage = KEY_STORAGE_INCORE;
2435   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2436   memcpy(data->attrdata + data->attrdatalen, str, l);
2437   repodata_set(data, solvid, &key, data->attrdatalen);
2438   data->attrdatalen += l;
2439 }
2440
2441 void
2442 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2443                       const char *str)
2444 {
2445   unsigned char buf[64];
2446   int l;
2447
2448   if (!(l = solv_chksum_len(type)))
2449     return;
2450   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2451     return;
2452   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2453 }
2454
2455 const char *
2456 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2457 {
2458   int l;
2459
2460   if (!(l = solv_chksum_len(type)))
2461     return "";
2462   return pool_bin2hex(data->repo->pool, buf, l);
2463 }
2464
2465 /* rpm filenames don't contain the epoch, so strip it */
2466 static inline const char *
2467 evrid2vrstr(Pool *pool, Id evrid)
2468 {
2469   const char *p, *evr = pool_id2str(pool, evrid);
2470   if (!evr)
2471     return evr;
2472   for (p = evr; *p >= '0' && *p <= '9'; p++)
2473     ;
2474   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2475 }
2476
2477 static inline void
2478 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2479 {
2480   Id id;
2481   if (data->localpool)
2482     id = stringpool_strn2id(&data->spool, str, l, 1);
2483   else
2484     id = pool_strn2id(data->repo->pool, str, l, 1);
2485   repodata_set_id(data, solvid, keyname, id);
2486 }
2487
2488 static inline void
2489 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2490 {
2491   if (!str[l])
2492     repodata_set_str(data, solvid, keyname, str);
2493   else
2494     {
2495       char *s = solv_strdup(str);
2496       s[l] = 0;
2497       repodata_set_str(data, solvid, keyname, s);
2498       free(s);
2499     }
2500 }
2501
2502 void
2503 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2504 {
2505   Pool *pool = data->repo->pool;
2506   Solvable *s;
2507   const char *str, *fp;
2508   int l = 0;
2509
2510   if (medianr)
2511     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2512   if (!dir)
2513     {
2514       if ((dir = strrchr(file, '/')) != 0)
2515         {
2516           l = dir - file;
2517           dir = file;
2518           file = dir + l + 1;
2519           if (!l)
2520             l++;
2521         }
2522     }
2523   else
2524     l = strlen(dir);
2525   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2526     {
2527       dir += 2;
2528       l -= 2;
2529     }
2530   if (l == 1 && dir[0] == '.')
2531     l = 0;
2532   s = pool->solvables + solvid;
2533   if (dir && l)
2534     {
2535       str = pool_id2str(pool, s->arch);
2536       if (!strncmp(dir, str, l) && !str[l])
2537         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2538       else
2539         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2540     }
2541   fp = file;
2542   str = pool_id2str(pool, s->name);
2543   l = strlen(str);
2544   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2545     {
2546       fp += l + 1;
2547       str = evrid2vrstr(pool, s->evr);
2548       l = strlen(str);
2549       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2550         {
2551           fp += l + 1;
2552           str = pool_id2str(pool, s->arch);
2553           l = strlen(str);
2554           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2555             {
2556               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2557               return;
2558             }
2559         }
2560     }
2561   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2562 }
2563
2564 /* XXX: medianr is currently not stored */
2565 void
2566 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2567 {
2568   int l = 0;
2569   const char *evr, *suf, *s;
2570
2571   if (!dir)
2572     {
2573       if ((dir = strrchr(file, '/')) != 0)
2574         {
2575           l = dir - file;
2576           dir = file;
2577           file = dir + l + 1;
2578           if (!l)
2579             l++;
2580         }
2581     }
2582   else
2583     l = strlen(dir);
2584   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2585     {
2586       dir += 2;
2587       l -= 2;
2588     }
2589   if (l == 1 && dir[0] == '.')
2590     l = 0;
2591   if (dir && l)
2592     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2593   evr = strchr(file, '-');
2594   if (evr)
2595     {
2596       for (s = evr - 1; s > file; s--)
2597         if (*s == '-')
2598           {
2599             evr = s;
2600             break;
2601           }
2602     }
2603   suf = strrchr(file, '.');
2604   if (suf)
2605     {
2606       for (s = suf - 1; s > file; s--)
2607         if (*s == '.')
2608           {
2609             suf = s;
2610             break;
2611           }
2612       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2613         {
2614           /* We accept one more item as suffix.  */
2615           for (s = suf - 1; s > file; s--)
2616             if (*s == '.')
2617               {
2618                 suf = s;
2619                 break;
2620               }
2621         }
2622     }
2623   if (!evr)
2624     suf = 0;
2625   if (suf && evr && suf < evr)
2626     suf = 0;
2627   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2628   if (evr)
2629     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2630   if (suf)
2631     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2632 }
2633
2634 void
2635 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2636 {
2637   Pool *pool = data->repo->pool;
2638   Solvable *s = pool->solvables + solvid;
2639   const char *p, *sevr, *sarch, *name, *evr;
2640
2641   p = strrchr(sourcepkg, '.');
2642   if (!p || strcmp(p, ".rpm") != 0)
2643     {
2644       if (*sourcepkg)
2645         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2646       return;
2647     }
2648   p--;
2649   while (p > sourcepkg && *p != '.')
2650     p--;
2651   if (*p != '.' || p == sourcepkg)
2652     return;
2653   sarch = p-- + 1;
2654   while (p > sourcepkg && *p != '-')
2655     p--;
2656   if (*p != '-' || p == sourcepkg)
2657     return;
2658   p--;
2659   while (p > sourcepkg && *p != '-')
2660     p--;
2661   if (*p != '-' || p == sourcepkg)
2662     return;
2663   sevr = p + 1;
2664   pool = s->repo->pool;
2665
2666   name = pool_id2str(pool, s->name);
2667   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2668     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2669   else
2670     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2671
2672   evr = evrid2vrstr(pool, s->evr);
2673   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2674     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2675   else
2676     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2677
2678   if (!strcmp(sarch, "src.rpm"))
2679     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2680   else if (!strcmp(sarch, "nosrc.rpm"))
2681     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2682   else
2683     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2684 }
2685
2686 void
2687 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2688 {
2689   Repokey key;
2690   int i;
2691
2692   key.name = keyname;
2693   key.type = REPOKEY_TYPE_IDARRAY;
2694   key.size = 0;
2695   key.storage = KEY_STORAGE_INCORE;
2696   repodata_set(data, solvid, &key, data->attriddatalen);
2697   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2698   for (i = 0; i < q->count; i++)
2699     data->attriddata[data->attriddatalen++] = q->elements[i];
2700   data->attriddata[data->attriddatalen++] = 0;
2701 }
2702
2703 void
2704 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2705 {
2706   assert(dir);
2707 #if 0
2708 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2709 #endif
2710   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2711   data->attriddata[data->attriddatalen++] = dir;
2712   data->attriddata[data->attriddatalen++] = num;
2713   data->attriddata[data->attriddatalen++] = num2;
2714   data->attriddata[data->attriddatalen++] = 0;
2715 }
2716
2717 void
2718 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2719 {
2720   Id stroff;
2721   int l;
2722
2723   assert(dir);
2724   l = strlen(str) + 1;
2725   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2726   memcpy(data->attrdata + data->attrdatalen, str, l);
2727   stroff = data->attrdatalen;
2728   data->attrdatalen += l;
2729
2730 #if 0
2731 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2732 #endif
2733   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2734   data->attriddata[data->attriddatalen++] = dir;
2735   data->attriddata[data->attriddatalen++] = stroff;
2736   data->attriddata[data->attriddatalen++] = 0;
2737 }
2738
2739 void
2740 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2741 {
2742 #if 0
2743 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2744 #endif
2745   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2746   data->attriddata[data->attriddatalen++] = id;
2747   data->attriddata[data->attriddatalen++] = 0;
2748 }
2749
2750 void
2751 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2752                            const char *str)
2753 {
2754   Id id;
2755   if (data->localpool)
2756     id = stringpool_str2id(&data->spool, str, 1);
2757   else
2758     id = pool_str2id(data->repo->pool, str, 1);
2759   repodata_add_idarray(data, solvid, keyname, id);
2760 }
2761
2762 void
2763 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2764 {
2765   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2766   data->attriddata[data->attriddatalen++] = ghandle;
2767   data->attriddata[data->attriddatalen++] = 0;
2768 }
2769
2770 void
2771 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2772 {
2773   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2774   data->attriddata[data->attriddatalen++] = ghandle;
2775   data->attriddata[data->attriddatalen++] = 0;
2776 }
2777
2778 void
2779 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2780 {
2781   Id *pp, *ap, **app;
2782   app = repodata_get_attrp(data, solvid);
2783   ap = *app;
2784   if (!ap)
2785     return;
2786   for (; *ap; ap += 2)
2787     if (data->keys[*ap].name == keyname)
2788       break;
2789   if (!*ap)
2790     return;
2791   pp = ap;
2792   ap += 2;
2793   for (; *ap; ap += 2)
2794     {
2795       if (data->keys[*ap].name == keyname)
2796         continue;
2797       *pp++ = ap[0];
2798       *pp++ = ap[1];
2799     }
2800   *pp = 0;
2801 }
2802
2803 /* XXX: does not work correctly, needs fix in iterators! */
2804 void
2805 repodata_unset(Repodata *data, Id solvid, Id keyname)
2806 {
2807   Repokey key;
2808   key.name = keyname;
2809   key.type = REPOKEY_TYPE_DELETED;
2810   key.size = 0;
2811   key.storage = KEY_STORAGE_INCORE;
2812   repodata_set(data, solvid, &key, 0);
2813 }
2814
2815 /* add all (uninternalized) attrs from src to dest */
2816 void
2817 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2818 {
2819   Id *keyp;
2820   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2821     return;
2822   for (; *keyp; keyp += 2)
2823     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2824 }
2825
2826 /* add some (uninternalized) attrs from src to dest */
2827 void
2828 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2829 {
2830   Id *keyp;
2831   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2832     return;
2833   for (; *keyp; keyp += 2)
2834     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2835       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2836 }
2837
2838 /* swap (uninternalized) attrs from src and dest */
2839 void
2840 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2841 {
2842   Id *tmpattrs;
2843   if (!data->attrs || dest == src)
2844     return;
2845   tmpattrs = data->attrs[dest - data->start];
2846   data->attrs[dest - data->start] = data->attrs[src - data->start];
2847   data->attrs[src - data->start] = tmpattrs;
2848 }
2849
2850
2851 /**********************************************************************/
2852
2853 /* TODO: unify with repo_write and repo_solv! */
2854
2855 #define EXTDATA_BLOCK 1023
2856
2857 struct extdata {
2858   unsigned char *buf;
2859   int len;
2860 };
2861
2862 static void
2863 data_addid(struct extdata *xd, Id sx)
2864 {
2865   unsigned int x = (unsigned int)sx;
2866   unsigned char *dp;
2867
2868   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2869   dp = xd->buf + xd->len;
2870
2871   if (x >= (1 << 14))
2872     {
2873       if (x >= (1 << 28))
2874         *dp++ = (x >> 28) | 128;
2875       if (x >= (1 << 21))
2876         *dp++ = (x >> 21) | 128;
2877       *dp++ = (x >> 14) | 128;
2878     }
2879   if (x >= (1 << 7))
2880     *dp++ = (x >> 7) | 128;
2881   *dp++ = x & 127;
2882   xd->len = dp - xd->buf;
2883 }
2884
2885 static void
2886 data_addid64(struct extdata *xd, unsigned long long x)
2887 {
2888   if (x >= 0x100000000)
2889     {
2890       if ((x >> 35) != 0)
2891         {
2892           data_addid(xd, (Id)(x >> 35));
2893           xd->buf[xd->len - 1] |= 128;
2894         }
2895       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2896       xd->buf[xd->len - 5] = (x >> 28) | 128;
2897     }
2898   else
2899     data_addid(xd, (Id)x);
2900 }
2901
2902 static void
2903 data_addideof(struct extdata *xd, Id sx, int eof)
2904 {
2905   unsigned int x = (unsigned int)sx;
2906   unsigned char *dp;
2907
2908   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2909   dp = xd->buf + xd->len;
2910
2911   if (x >= (1 << 13))
2912     {
2913       if (x >= (1 << 27))
2914         *dp++ = (x >> 27) | 128;
2915       if (x >= (1 << 20))
2916         *dp++ = (x >> 20) | 128;
2917       *dp++ = (x >> 13) | 128;
2918     }
2919   if (x >= (1 << 6))
2920     *dp++ = (x >> 6) | 128;
2921   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2922   xd->len = dp - xd->buf;
2923 }
2924
2925 static void
2926 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2927 {
2928   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2929   memcpy(xd->buf + xd->len, blob, len);
2930   xd->len += len;
2931 }
2932
2933 /*********************************/
2934
2935 /* internalalize some key into incore/vincore data */
2936
2937 static void
2938 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2939                        struct extdata *newvincore,
2940                        Id *schema,
2941                        Repokey *key, Id val)
2942 {
2943   Id *ida;
2944   struct extdata *xd;
2945   unsigned int oldvincorelen = 0;
2946   Id schemaid, *sp;
2947
2948   xd = newincore;
2949   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2950     {
2951       xd = newvincore;
2952       oldvincorelen = xd->len;
2953     }
2954   switch (key->type)
2955     {
2956     case REPOKEY_TYPE_VOID:
2957     case REPOKEY_TYPE_CONSTANT:
2958     case REPOKEY_TYPE_CONSTANTID:
2959       break;
2960     case REPOKEY_TYPE_STR:
2961       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2962       break;
2963     case REPOKEY_TYPE_MD5:
2964       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2965       break;
2966     case REPOKEY_TYPE_SHA1:
2967       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2968       break;
2969     case REPOKEY_TYPE_SHA224:
2970       data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
2971       break;
2972     case REPOKEY_TYPE_SHA256:
2973       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2974       break;
2975     case REPOKEY_TYPE_SHA384:
2976       data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
2977       break;
2978     case REPOKEY_TYPE_SHA512:
2979       data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
2980       break;
2981     case REPOKEY_TYPE_NUM:
2982       if (val & 0x80000000)
2983         {
2984           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2985           break;
2986         }
2987       /* FALLTHROUGH */
2988     case REPOKEY_TYPE_ID:
2989     case REPOKEY_TYPE_DIR:
2990       data_addid(xd, val);
2991       break;
2992     case REPOKEY_TYPE_BINARY:
2993       {
2994         Id len;
2995         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2996         dp += (unsigned int)len;
2997         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2998       }
2999       break;
3000     case REPOKEY_TYPE_IDARRAY:
3001       for (ida = data->attriddata + val; *ida; ida++)
3002         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
3003       break;
3004     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3005       for (ida = data->attriddata + val; *ida; ida += 3)
3006         {
3007           data_addid(xd, ida[0]);
3008           data_addid(xd, ida[1]);
3009           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
3010         }
3011       break;
3012     case REPOKEY_TYPE_DIRSTRARRAY:
3013       for (ida = data->attriddata + val; *ida; ida += 2)
3014         {
3015           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
3016           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
3017         }
3018       break;
3019     case REPOKEY_TYPE_FIXARRAY:
3020       {
3021         int num = 0;
3022         schemaid = 0;
3023         for (ida = data->attriddata + val; *ida; ida++)
3024           {
3025             Id *kp;
3026             sp = schema;
3027             kp = data->xattrs[-*ida];
3028             if (!kp)
3029               continue;
3030             num++;
3031             for (;*kp; kp += 2)
3032               *sp++ = *kp;
3033             *sp = 0;
3034             if (!schemaid)
3035               schemaid = repodata_schema2id(data, schema, 1);
3036             else if (schemaid != repodata_schema2id(data, schema, 0))
3037               {
3038                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
3039                 exit(1);
3040               }
3041           }
3042         if (!num)
3043           break;
3044         data_addid(xd, num);
3045         data_addid(xd, schemaid);
3046         for (ida = data->attriddata + val; *ida; ida++)
3047           {
3048             Id *kp = data->xattrs[-*ida];
3049             if (!kp)
3050               continue;
3051             for (;*kp; kp += 2)
3052               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3053           }
3054         break;
3055       }
3056     case REPOKEY_TYPE_FLEXARRAY:
3057       {
3058         int num = 0;
3059         for (ida = data->attriddata + val; *ida; ida++)
3060           num++;
3061         data_addid(xd, num);
3062         for (ida = data->attriddata + val; *ida; ida++)
3063           {
3064             Id *kp = data->xattrs[-*ida];
3065             if (!kp)
3066               {
3067                 data_addid(xd, 0);      /* XXX */
3068                 continue;
3069               }
3070             sp = schema;
3071             for (;*kp; kp += 2)
3072               *sp++ = *kp;
3073             *sp = 0;
3074             schemaid = repodata_schema2id(data, schema, 1);
3075             data_addid(xd, schemaid);
3076             kp = data->xattrs[-*ida];
3077             for (;*kp; kp += 2)
3078               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3079           }
3080         break;
3081       }
3082     default:
3083       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
3084       exit(1);
3085     }
3086   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3087     {
3088       /* put offset/len in incore */
3089       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
3090       oldvincorelen = xd->len - oldvincorelen;
3091       data_addid(newincore, oldvincorelen);
3092     }
3093 }
3094
3095 void
3096 repodata_internalize(Repodata *data)
3097 {
3098   Repokey *key, solvkey;
3099   Id entry, nentry;
3100   Id schemaid, keyid, *schema, *sp, oldschema, *keyp, *seen;
3101   int schemaidx;
3102   unsigned char *dp, *ndp;
3103   int newschema, oldcount;
3104   struct extdata newincore;
3105   struct extdata newvincore;
3106   Id solvkeyid;
3107
3108   if (!data->attrs && !data->xattrs)
3109     return;
3110
3111   newvincore.buf = data->vincore;
3112   newvincore.len = data->vincorelen;
3113
3114   /* find the solvables key, create if needed */
3115   memset(&solvkey, 0, sizeof(solvkey));
3116   solvkey.name = REPOSITORY_SOLVABLES;
3117   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3118   solvkey.size = 0;
3119   solvkey.storage = KEY_STORAGE_INCORE;
3120   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3121
3122   schema = solv_malloc2(data->nkeys, sizeof(Id));
3123   seen = solv_malloc2(data->nkeys, sizeof(Id));
3124
3125   /* Merge the data already existing (in data->schemata, ->incoredata and
3126      friends) with the new attributes in data->attrs[].  */
3127   nentry = data->end - data->start;
3128   memset(&newincore, 0, sizeof(newincore));
3129   data_addid(&newincore, 0);    /* start data at offset 1 */
3130
3131   data->mainschema = 0;
3132   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3133
3134   /* join entry data */
3135   /* we start with the meta data, entry -1 */
3136   for (entry = -1; entry < nentry; entry++)
3137     {
3138       memset(seen, 0, data->nkeys * sizeof(Id));
3139       oldschema = 0;
3140       dp = data->incoredata;
3141       if (dp)
3142         {
3143           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3144           dp = data_read_id(dp, &oldschema);
3145         }
3146 #if 0
3147 fprintf(stderr, "oldschema %d\n", oldschema);
3148 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3149 fprintf(stderr, "schemadata %p\n", data->schemadata);
3150 #endif
3151       /* seen: -1: old data  0: skipped  >0: id + 1 */
3152       newschema = 0;
3153       oldcount = 0;
3154       sp = schema;
3155       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3156         {
3157           if (seen[*keyp])
3158             {
3159               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3160               exit(1);
3161             }
3162           seen[*keyp] = -1;
3163           *sp++ = *keyp;
3164           oldcount++;
3165         }
3166       if (entry >= 0)
3167         keyp = data->attrs ? data->attrs[entry] : 0;
3168       else
3169         {
3170           /* strip solvables key */
3171           *sp = 0;
3172           for (sp = keyp = schema; *sp; sp++)
3173             if (*sp != solvkeyid)
3174               *keyp++ = *sp;
3175             else
3176               oldcount--;
3177           sp = keyp;
3178           seen[solvkeyid] = 0;
3179           keyp = data->xattrs ? data->xattrs[1] : 0;
3180         }
3181       if (keyp)
3182         for (; *keyp; keyp += 2)
3183           {
3184             if (!seen[*keyp])
3185               {
3186                 newschema = 1;
3187                 *sp++ = *keyp;
3188               }
3189             seen[*keyp] = keyp[1] + 1;
3190           }
3191       if (entry < 0 && data->end != data->start)
3192         {
3193           *sp++ = solvkeyid;
3194           newschema = 1;
3195         }
3196       *sp = 0;
3197       if (newschema)
3198         /* Ideally we'd like to sort the new schema here, to ensure
3199            schema equality independend of the ordering.  We can't do that
3200            yet.  For once see below (old ids need to come before new ids).
3201            An additional difficulty is that we also need to move
3202            the values with the keys.  */
3203         schemaid = repodata_schema2id(data, schema, 1);
3204       else
3205         schemaid = oldschema;
3206
3207
3208       /* Now create data blob.  We walk through the (possibly new) schema
3209          and either copy over old data, or insert the new.  */
3210       /* XXX Here we rely on the fact that the (new) schema has the form
3211          o1 o2 o3 o4 ... | n1 n2 n3 ...
3212          (oX being the old keyids (possibly overwritten), and nX being
3213           the new keyids).  This rules out sorting the keyids in order
3214          to ensure a small schema count.  */
3215       if (entry >= 0)
3216         data->incoreoffset[entry] = newincore.len;
3217       data_addid(&newincore, schemaid);
3218       if (entry == -1)
3219         {
3220           data->mainschema = schemaid;
3221           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3222         }
3223       /* we don't use a pointer to the schemadata here as repodata_serialize_key
3224        * may call repodata_schema2id() which might realloc our schemadata */
3225       for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
3226         {
3227           if (entry == -1)
3228             data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
3229           if (keyid == solvkeyid)
3230             {
3231               /* add flexarray entry count */
3232               data_addid(&newincore, data->end - data->start);
3233               break;
3234             }
3235           key = data->keys + keyid;
3236 #if 0
3237           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, pool_id2str(data->repo->pool, key->name), pool_id2str(data->repo->pool, key->type));
3238 #endif
3239           ndp = dp;
3240           if (oldcount)
3241             {
3242               /* Skip the data associated with this old key.  */
3243               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3244                 {
3245                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3246                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3247                 }
3248               else if (key->storage == KEY_STORAGE_INCORE)
3249                 ndp = data_skip_key(data, dp, key);
3250               oldcount--;
3251             }
3252           if (seen[keyid] == -1)
3253             {
3254               /* If this key was an old one _and_ was not overwritten with
3255                  a different value copy over the old value (we skipped it
3256                  above).  */
3257               if (dp != ndp)
3258                 data_addblob(&newincore, dp, ndp - dp);
3259               seen[keyid] = 0;
3260             }
3261           else if (seen[keyid])
3262             {
3263               /* Otherwise we have a new value.  Parse it into the internal form.  */
3264               repodata_serialize_key(data, &newincore, &newvincore, schema, key, seen[keyid] - 1);
3265             }
3266           dp = ndp;
3267         }
3268       if (entry >= 0 && data->attrs && data->attrs[entry])
3269         data->attrs[entry] = solv_free(data->attrs[entry]);
3270     }
3271   /* free all xattrs */
3272   for (entry = 0; entry < data->nxattrs; entry++)
3273     if (data->xattrs[entry])
3274       solv_free(data->xattrs[entry]);
3275   data->xattrs = solv_free(data->xattrs);
3276   data->nxattrs = 0;
3277
3278   data->lasthandle = 0;
3279   data->lastkey = 0;
3280   data->lastdatalen = 0;
3281   solv_free(schema);
3282   solv_free(seen);
3283   repodata_free_schemahash(data);
3284
3285   solv_free(data->incoredata);
3286   data->incoredata = newincore.buf;
3287   data->incoredatalen = newincore.len;
3288   data->incoredatafree = 0;
3289
3290   solv_free(data->vincore);
3291   data->vincore = newvincore.buf;
3292   data->vincorelen = newvincore.len;
3293
3294   data->attrs = solv_free(data->attrs);
3295   data->attrdata = solv_free(data->attrdata);
3296   data->attriddata = solv_free(data->attriddata);
3297   data->attrnum64data = solv_free(data->attrnum64data);
3298   data->attrdatalen = 0;
3299   data->attriddatalen = 0;
3300   data->attrnum64datalen = 0;
3301 }
3302
3303 void
3304 repodata_disable_paging(Repodata *data)
3305 {
3306   if (maybe_load_repodata(data, 0))
3307     {
3308       repopagestore_disable_paging(&data->store);
3309       data->storestate++;
3310     }
3311 }
3312
3313 static void
3314 repodata_load_stub(Repodata *data)
3315 {
3316   Repo *repo = data->repo;
3317   Pool *pool = repo->pool;
3318   int r, i;
3319   struct _Pool_tmpspace oldtmpspace;
3320   Datapos oldpos;
3321
3322   if (!pool->loadcallback)
3323     {
3324       data->state = REPODATA_ERROR;
3325       return;
3326     }
3327   data->state = REPODATA_LOADING;
3328
3329   /* save tmp space and pos */
3330   oldtmpspace = pool->tmpspace;
3331   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3332   oldpos = pool->pos;
3333
3334   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3335
3336   /* restore tmp space and pos */
3337   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3338     solv_free(pool->tmpspace.buf[i]);
3339   pool->tmpspace = oldtmpspace;
3340   if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
3341     memset(&oldpos, 0, sizeof(oldpos));
3342   pool->pos = oldpos;
3343
3344   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3345 }
3346
3347 static inline void
3348 repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
3349 {
3350   Repokey xkey;
3351
3352   xkey.name = keyname;
3353   xkey.type = keytype;
3354   xkey.storage = KEY_STORAGE_INCORE;
3355   xkey.size = 0;
3356   repodata_key2id(data, &xkey, 1);
3357 }
3358
3359 static Repodata *
3360 repodata_add_stub(Repodata **datap)
3361 {
3362   Repodata *data = *datap;
3363   Repo *repo = data->repo;
3364   Id repodataid = data - repo->repodata;
3365   Repodata *sdata = repo_add_repodata(repo, 0);
3366   data = repo->repodata + repodataid;
3367   if (data->end > data->start)
3368     repodata_extend_block(sdata, data->start, data->end - data->start);
3369   sdata->state = REPODATA_STUB;
3370   sdata->loadcallback = repodata_load_stub;
3371   *datap = data;
3372   return sdata;
3373 }
3374
3375 Repodata *
3376 repodata_create_stubs(Repodata *data)
3377 {
3378   Repo *repo = data->repo;
3379   Pool *pool = repo->pool;
3380   Repodata *sdata;
3381   int *stubdataids;
3382   Dataiterator di;
3383   Id xkeyname = 0;
3384   int i, cnt = 0;
3385
3386   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3387   while (dataiterator_step(&di))
3388     if (di.data == data)
3389       cnt++;
3390   dataiterator_free(&di);
3391   if (!cnt)
3392     return data;
3393   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3394   for (i = 0; i < cnt; i++)
3395     {
3396       sdata = repodata_add_stub(&data);
3397       stubdataids[i] = sdata - repo->repodata;
3398     }
3399   i = 0;
3400   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3401   sdata = 0;
3402   while (dataiterator_step(&di))
3403     {
3404       if (di.data != data)
3405         continue;
3406       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3407         {
3408           dataiterator_entersub(&di);
3409           sdata = repo->repodata + stubdataids[i++];
3410           xkeyname = 0;
3411           continue;
3412         }
3413       switch (di.key->type)
3414         {
3415         case REPOKEY_TYPE_ID:
3416           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3417           break;
3418         case REPOKEY_TYPE_CONSTANTID:
3419           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3420           break;
3421         case REPOKEY_TYPE_STR:
3422           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3423           break;
3424         case REPOKEY_TYPE_VOID:
3425           repodata_set_void(sdata, SOLVID_META, di.key->name);
3426           break;
3427         case REPOKEY_TYPE_NUM:
3428           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3429           break;
3430         case REPOKEY_TYPE_MD5:
3431         case REPOKEY_TYPE_SHA1:
3432         case REPOKEY_TYPE_SHA224:
3433         case REPOKEY_TYPE_SHA256:
3434         case REPOKEY_TYPE_SHA384:
3435         case REPOKEY_TYPE_SHA512:
3436           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3437           break;
3438         case REPOKEY_TYPE_IDARRAY:
3439           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3440           if (di.key->name == REPOSITORY_KEYS)
3441             {
3442               if (!xkeyname)
3443                 {
3444                   if (!di.kv.eof)
3445                     xkeyname = di.kv.id;
3446                 }
3447               else
3448                 {
3449                   repodata_add_stubkey(sdata, xkeyname, di.kv.id);
3450                   xkeyname = 0;
3451                 }
3452             }
3453           break;
3454         default:
3455           break;
3456         }
3457     }
3458   dataiterator_free(&di);
3459   for (i = 0; i < cnt; i++)
3460     repodata_internalize(repo->repodata + stubdataids[i]);
3461   solv_free(stubdataids);
3462   return data;
3463 }
3464
3465 unsigned int
3466 repodata_memused(Repodata *data)
3467 {
3468   return data->incoredatalen + data->vincorelen;
3469 }
3470