Imported Upstream version 0.6.5
[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 ((unsigned int)off + len > data->vincorelen)
483         return 0;
484       return data->vincore + off;
485     }
486   if ((unsigned int)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_CHKSUM_TYPES:
747       break;
748     default:
749       return 0;
750     }
751   *typep = key->type;
752   return dp;
753 }
754
755 int
756 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
757 {
758   unsigned char *dp;
759   Repokey *key;
760   Id id;
761   int eof = 0;
762
763   queue_empty(q);
764   dp = find_key_data(data, solvid, keyname, &key);
765   if (!dp)
766     return 0;
767   if (key->type != REPOKEY_TYPE_IDARRAY)
768     return 0;
769   for (;;)
770     {
771       dp = data_read_ideof(dp, &id, &eof);
772       queue_push(q, id);
773       if (eof)
774         break;
775     }
776   return 1;
777 }
778
779 const void *
780 repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
781 {
782   unsigned char *dp;
783   Repokey *key;
784   Id len;
785
786   dp = find_key_data(data, solvid, keyname, &key);
787   if (!dp || key->type != REPOKEY_TYPE_BINARY)
788     {
789       *lenp = 0;
790       return 0;
791     }
792   dp = data_read_id(dp, &len);
793   *lenp = len;
794   return dp;
795 }
796
797 Id
798 repodata_globalize_id(Repodata *data, Id id, int create)
799 {
800   if (!id || !data || !data->localpool)
801     return id;
802   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
803 }
804
805 Id
806 repodata_localize_id(Repodata *data, Id id, int create)
807 {
808   if (!id || !data || !data->localpool)
809     return id;
810   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
811 }
812
813 Id
814 repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
815 {
816   if (!id || !data || !fromdata)
817     return id;
818   if (!data->localpool || !fromdata->localpool)
819     {
820       if (fromdata->localpool)
821         id = repodata_globalize_id(fromdata, id, create);
822       if (data->localpool)
823         id = repodata_localize_id(data, id, create);
824       return id;
825     }
826   /* localpool is set in both data and fromdata */
827   return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
828 }
829
830 Id
831 repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
832 {
833   Id *ap;
834   if (!data->attrs)
835     return 0;
836   ap = data->attrs[solvid - data->start];
837   if (!ap)
838     return 0;
839   for (; *ap; ap += 2)
840     {
841       if (data->keys[*ap].name != keyname)
842         continue;
843       if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
844         return voidid;
845       if (data->keys[*ap].type == REPOKEY_TYPE_ID)
846         return ap[1];
847       return 0;
848     }
849   return 0;
850 }
851
852
853 /************************************************************************
854  * data search
855  */
856
857
858 const char *
859 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
860 {
861   switch (key->type)
862     {
863     case REPOKEY_TYPE_ID:
864     case REPOKEY_TYPE_CONSTANTID:
865     case REPOKEY_TYPE_IDARRAY:
866       if (data && data->localpool)
867         kv->str = stringpool_id2str(&data->spool, kv->id);
868       else
869         kv->str = pool_id2str(pool, kv->id);
870       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
871         {
872           const char *s;
873           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
874             ;
875           if (*s == ':' && s > kv->str)
876             kv->str = s + 1;
877         }
878       return kv->str;
879     case REPOKEY_TYPE_STR:
880       return kv->str;
881     case REPOKEY_TYPE_DIRSTRARRAY:
882       if (!(flags & SEARCH_FILES))
883         return kv->str; /* match just the basename */
884       if (kv->num)
885         return kv->str; /* already stringified */
886       /* Put the full filename into kv->str.  */
887       kv->str = repodata_dir2str(data, kv->id, kv->str);
888       kv->num = 1;      /* mark stringification */
889       return kv->str;
890     case_CHKSUM_TYPES:
891       if (!(flags & SEARCH_CHECKSUMS))
892         return 0;       /* skip em */
893       if (kv->num)
894         return kv->str; /* already stringified */
895       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
896       kv->num = 1;      /* mark stringification */
897       return kv->str;
898     default:
899       return 0;
900     }
901 }
902
903
904 struct subschema_data {
905   Solvable *s;
906   void *cbdata;
907   KeyValue *parent;
908 };
909
910 /* search a specific repodata */
911 void
912 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
913 {
914   Id schema;
915   Repokey *key;
916   Id keyid, *kp, *keyp;
917   unsigned char *dp, *ddp;
918   int onekey = 0;
919   int stop;
920   KeyValue kv;
921   Solvable *s;
922
923   if (!maybe_load_repodata(data, keyname))
924     return;
925   if (solvid == SOLVID_SUBSCHEMA)
926     {
927       struct subschema_data *subd = cbdata;
928       cbdata = subd->cbdata;
929       s = subd->s;
930       schema = subd->parent->id;
931       dp = (unsigned char *)subd->parent->str;
932       kv.parent = subd->parent;
933     }
934   else
935     {
936       schema = 0;
937       dp = solvid2data(data, solvid, &schema);
938       if (!dp)
939         return;
940       s = data->repo->pool->solvables + solvid;
941       kv.parent = 0;
942     }
943   keyp = data->schemadata + data->schemata[schema];
944   if (keyname)
945     {
946       /* search for a specific key */
947       for (kp = keyp; *kp; kp++)
948         if (data->keys[*kp].name == keyname)
949           break;
950       if (!*kp)
951         return;
952       dp = forward_to_key(data, *kp, keyp, dp);
953       if (!dp)
954         return;
955       keyp = kp;
956       onekey = 1;
957     }
958   while ((keyid = *keyp++) != 0)
959     {
960       stop = 0;
961       key = data->keys + keyid;
962       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
963
964       if (key->type == REPOKEY_TYPE_DELETED)
965         continue;
966       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
967         {
968           struct subschema_data subd;
969           int nentries;
970           Id schema = 0;
971
972           subd.cbdata = cbdata;
973           subd.s = s;
974           subd.parent = &kv;
975           ddp = data_read_id(ddp, &nentries);
976           kv.num = nentries;
977           kv.entry = 0;
978           kv.eof = 0;
979           while (ddp && nentries > 0)
980             {
981               if (!--nentries)
982                 kv.eof = 1;
983               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
984                 ddp = data_read_id(ddp, &schema);
985               kv.id = schema;
986               kv.str = (char *)ddp;
987               stop = callback(cbdata, s, data, key, &kv);
988               if (stop > SEARCH_NEXT_KEY)
989                 return;
990               if (stop && stop != SEARCH_ENTERSUB)
991                 break;
992               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
993                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
994               ddp = data_skip_schema(data, ddp, schema);
995               kv.entry++;
996             }
997           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
998             {
999               /* sentinel */
1000               kv.eof = 2;
1001               kv.str = (char *)ddp;
1002               stop = callback(cbdata, s, data, key, &kv);
1003               if (stop > SEARCH_NEXT_KEY)
1004                 return;
1005             }
1006           if (onekey)
1007             return;
1008           continue;
1009         }
1010       kv.entry = 0;
1011       do
1012         {
1013           ddp = data_fetch(ddp, &kv, key);
1014           if (!ddp)
1015             break;
1016           stop = callback(cbdata, s, data, key, &kv);
1017           kv.entry++;
1018         }
1019       while (!kv.eof && !stop);
1020       if (onekey || stop > SEARCH_NEXT_KEY)
1021         return;
1022     }
1023 }
1024
1025 void
1026 repodata_setpos_kv(Repodata *data, KeyValue *kv)
1027 {
1028   Pool *pool = data->repo->pool;
1029   if (!kv)
1030     pool_clear_pos(pool);
1031   else
1032     {
1033       pool->pos.repo = data->repo;
1034       pool->pos.repodataid = data - data->repo->repodata;
1035       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
1036       pool->pos.schema = kv->id;
1037     }
1038 }
1039
1040 /************************************************************************
1041  * data iterator functions
1042  */
1043
1044 static inline Id *
1045 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1046 {
1047   kv->id = keyname;
1048   switch (keyname)
1049     {
1050     case SOLVABLE_NAME:
1051       kv->eof = 1;
1052       return &s->name;
1053     case SOLVABLE_ARCH:
1054       kv->eof = 1;
1055       return &s->arch;
1056     case SOLVABLE_EVR:
1057       kv->eof = 1;
1058       return &s->evr;
1059     case SOLVABLE_VENDOR:
1060       kv->eof = 1;
1061       return &s->vendor;
1062     case SOLVABLE_PROVIDES:
1063       kv->eof = 0;
1064       return s->provides ? s->repo->idarraydata + s->provides : 0;
1065     case SOLVABLE_OBSOLETES:
1066       kv->eof = 0;
1067       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1068     case SOLVABLE_CONFLICTS:
1069       kv->eof = 0;
1070       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1071     case SOLVABLE_REQUIRES:
1072       kv->eof = 0;
1073       return s->requires ? s->repo->idarraydata + s->requires : 0;
1074     case SOLVABLE_RECOMMENDS:
1075       kv->eof = 0;
1076       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1077     case SOLVABLE_SUPPLEMENTS:
1078       kv->eof = 0;
1079       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1080     case SOLVABLE_SUGGESTS:
1081       kv->eof = 0;
1082       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1083     case SOLVABLE_ENHANCES:
1084       kv->eof = 0;
1085       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1086     case RPM_RPMDBID:
1087       kv->eof = 1;
1088       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1089     default:
1090       return 0;
1091     }
1092 }
1093
1094 int
1095 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1096 {
1097   match = match ? solv_strdup(match) : 0;
1098   ma->match = match;
1099   ma->flags = flags;
1100   ma->error = 0;
1101   ma->matchdata = 0;
1102   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1103     {
1104       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1105       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1106       if (ma->error)
1107         {
1108           solv_free(ma->matchdata);
1109           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1110         }
1111     }
1112   if ((flags & SEARCH_FILES) != 0 && match)
1113     {
1114       /* prepare basename check */
1115       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1116         {
1117           const char *p = strrchr(match, '/');
1118           ma->matchdata = (void *)(p ? p + 1 : match);
1119         }
1120       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1121         {
1122           const char *p;
1123           for (p = match + strlen(match) - 1; p >= match; p--)
1124             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1125               break;
1126           ma->matchdata = (void *)(p + 1);
1127         }
1128     }
1129   return ma->error;
1130 }
1131
1132 void
1133 datamatcher_free(Datamatcher *ma)
1134 {
1135   if (ma->match)
1136     ma->match = solv_free((char *)ma->match);
1137   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1138     {
1139       regfree(ma->matchdata);
1140       solv_free(ma->matchdata);
1141     }
1142   ma->matchdata = 0;
1143 }
1144
1145 int
1146 datamatcher_match(Datamatcher *ma, const char *str)
1147 {
1148   int l;
1149   switch ((ma->flags & SEARCH_STRINGMASK))
1150     {
1151     case SEARCH_SUBSTRING:
1152       if (ma->flags & SEARCH_NOCASE)
1153         return strcasestr(str, ma->match) != 0;
1154       else
1155         return strstr(str, ma->match) != 0;
1156     case SEARCH_STRING:
1157       if (ma->flags & SEARCH_NOCASE)
1158         return !strcasecmp(ma->match, str);
1159       else
1160         return !strcmp(ma->match, str);
1161     case SEARCH_STRINGSTART:
1162       if (ma->flags & SEARCH_NOCASE)
1163         return !strncasecmp(ma->match, str, strlen(ma->match));
1164       else
1165         return !strncmp(ma->match, str, strlen(ma->match));
1166     case SEARCH_STRINGEND:
1167       l = strlen(str) - strlen(ma->match);
1168       if (l < 0)
1169         return 0;
1170       if (ma->flags & SEARCH_NOCASE)
1171         return !strcasecmp(ma->match, str + l);
1172       else
1173         return !strcmp(ma->match, str + l);
1174     case SEARCH_GLOB:
1175       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1176     case SEARCH_REGEX:
1177       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1178     default:
1179       return 0;
1180     }
1181 }
1182
1183 /* check if the matcher can match the provides basename */
1184
1185 int
1186 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1187 {
1188   int l;
1189   const char *match = ma->matchdata;
1190   if (!match)
1191     return 1;
1192   switch (ma->flags & SEARCH_STRINGMASK)
1193     {
1194     case SEARCH_STRING:
1195       break;
1196     case SEARCH_STRINGEND:
1197       if (match != ma->match)
1198         break;          /* had slash, do exact match on basename */
1199       /* FALLTHROUGH */
1200     case SEARCH_GLOB:
1201       /* check if the basename ends with match */
1202       l = strlen(basename) - strlen(match);
1203       if (l < 0)
1204         return 0;
1205       basename += l;
1206       break;
1207     default:
1208       return 1; /* maybe matches */
1209     }
1210   if ((ma->flags & SEARCH_NOCASE) != 0)
1211     return !strcasecmp(match, basename);
1212   else
1213     return !strcmp(match, basename);
1214 }
1215
1216 int
1217 repodata_filelistfilter_matches(Repodata *data, const char *str)
1218 {
1219   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1220   /* for now hardcoded */
1221   if (strstr(str, "bin/"))
1222     return 1;
1223   if (!strncmp(str, "/etc/", 5))
1224     return 1;
1225   if (!strcmp(str, "/usr/lib/sendmail"))
1226     return 1;
1227   return 0;
1228 }
1229
1230
1231 enum {
1232   di_bye,
1233
1234   di_enterrepo,
1235   di_entersolvable,
1236   di_enterrepodata,
1237   di_enterschema,
1238   di_enterkey,
1239
1240   di_nextattr,
1241   di_nextkey,
1242   di_nextrepodata,
1243   di_nextsolvable,
1244   di_nextrepo,
1245
1246   di_enterarray,
1247   di_nextarrayelement,
1248
1249   di_entersub,
1250   di_leavesub,
1251
1252   di_nextsolvablekey,
1253   di_entersolvablekey,
1254   di_nextsolvableattr
1255 };
1256
1257 /* see dataiterator.h for documentation */
1258 int
1259 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1260 {
1261   memset(di, 0, sizeof(*di));
1262   di->pool = pool;
1263   di->flags = flags & ~SEARCH_THISSOLVID;
1264   if (!pool || (repo && repo->pool != pool))
1265     {
1266       di->state = di_bye;
1267       return -1;
1268     }
1269   if (match)
1270     {
1271       int error;
1272       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1273         {
1274           di->state = di_bye;
1275           return error;
1276         }
1277     }
1278   di->keyname = keyname;
1279   di->keynames[0] = keyname;
1280   dataiterator_set_search(di, repo, p);
1281   return 0;
1282 }
1283
1284 void
1285 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1286 {
1287   *di = *from;
1288   if (di->dupstr)
1289     {
1290       if (di->dupstr == di->kv.str)
1291         di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
1292       else
1293         {
1294           di->dupstr = 0;
1295           di->dupstrn = 0;
1296         }
1297     }
1298   memset(&di->matcher, 0, sizeof(di->matcher));
1299   if (from->matcher.match)
1300     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1301   if (di->nparents)
1302     {
1303       /* fix pointers */
1304       int i;
1305       for (i = 1; i < di->nparents; i++)
1306         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1307       di->kv.parent = &di->parents[di->nparents - 1].kv;
1308     }
1309 }
1310
1311 int
1312 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1313 {
1314   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1315   datamatcher_free(&di->matcher);
1316   memset(&di->matcher, 0, sizeof(di->matcher));
1317   if (match)
1318     {
1319       int error;
1320       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1321         {
1322           di->state = di_bye;
1323           return error;
1324         }
1325     }
1326   return 0;
1327 }
1328
1329 void
1330 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1331 {
1332   di->repo = repo;
1333   di->repoid = 0;
1334   di->flags &= ~SEARCH_THISSOLVID;
1335   di->nparents = 0;
1336   di->rootlevel = 0;
1337   di->repodataid = 1;
1338   if (!di->pool->urepos)
1339     {
1340       di->state = di_bye;
1341       return;
1342     }
1343   if (!repo)
1344     {
1345       di->repoid = 1;
1346       di->repo = di->pool->repos[di->repoid];
1347     }
1348   di->state = di_enterrepo;
1349   if (p)
1350     dataiterator_jump_to_solvid(di, p);
1351 }
1352
1353 void
1354 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1355 {
1356   di->nkeynames = 0;
1357   di->keyname = keyname;
1358   di->keynames[0] = keyname;
1359 }
1360
1361 void
1362 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1363 {
1364   int i;
1365
1366   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1367     {
1368       di->state = di_bye;       /* sorry */
1369       return;
1370     }
1371   for (i = di->nkeynames + 1; i > 0; i--)
1372     di->keynames[i] = di->keynames[i - 1];
1373   di->keynames[0] = di->keyname = keyname;
1374   di->nkeynames++;
1375 }
1376
1377 void
1378 dataiterator_free(Dataiterator *di)
1379 {
1380   if (di->matcher.match)
1381     datamatcher_free(&di->matcher);
1382   if (di->dupstr)
1383     solv_free(di->dupstr);
1384 }
1385
1386 static unsigned char *
1387 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1388 {
1389   Id *keyp;
1390   Repokey *keys = di->data->keys, *key;
1391   unsigned char *dp;
1392
1393   for (keyp = di->keyp; *keyp; keyp++)
1394     if (keys[*keyp].name == keyname)
1395       break;
1396   if (!*keyp)
1397     return 0;
1398   key = keys + *keyp;
1399   if (key->type == REPOKEY_TYPE_DELETED)
1400     return 0;
1401   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
1402     return 0;           /* get_data will not work, no need to forward */
1403   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1404   if (!dp)
1405     return 0;
1406   di->keyp = keyp;
1407   return dp;
1408 }
1409
1410 static inline int
1411 is_filelist_extension(Repodata *data)
1412 {
1413   int j;
1414   if (!repodata_precheck_keyname(data, SOLVABLE_FILELIST))
1415     return 0;
1416   for (j = 1; j < data->nkeys; j++)
1417     if (data->keys[j].name == SOLVABLE_FILELIST)
1418       break;
1419   if (j == data->nkeys)
1420     return 0;
1421   if (data->state != REPODATA_AVAILABLE)
1422     return 1;
1423   for (j = 1; j < data->nkeys; j++)
1424     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1425       return 0;
1426   return 1;
1427 }
1428
1429 static int
1430 dataiterator_filelistcheck(Dataiterator *di)
1431 {
1432   int j;
1433   int needcomplete = 0;
1434   Repodata *data = di->data;
1435
1436   if ((di->flags & SEARCH_COMPLETE_FILELIST) != 0)
1437     if (!di->matcher.match
1438        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1439            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1440        || !repodata_filelistfilter_matches(data, di->matcher.match))
1441       needcomplete = 1;
1442   if (data->state != REPODATA_AVAILABLE)
1443     return needcomplete ? 1 : 0;
1444   if (!needcomplete)
1445     {
1446       /* we don't need the complete filelist, so ignore all stubs */
1447       for (j = 1; j < data->nkeys; j++)
1448         if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1449           return 1;
1450       return 0;
1451     }
1452   else
1453     {
1454       /* we need the complete filelist. check if we habe a filtered filelist and there's
1455        * a extension with the complete filelist later on */
1456       for (j = 1; j < data->nkeys; j++)
1457         if (data->keys[j].name == SOLVABLE_FILELIST)
1458           break;
1459       if (j == data->nkeys)
1460         return 0;       /* does not have filelist */
1461       for (j = 1; j < data->nkeys; j++)
1462         if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1463           break;
1464       if (j == data->nkeys)
1465         return 1;       /* this is the externsion */
1466       while (data - data->repo->repodata + 1 < data->repo->nrepodata)
1467         {
1468           data++;
1469           if (is_filelist_extension(data))
1470             return 0;
1471         }
1472       return 1;
1473     }
1474 }
1475
1476 int
1477 dataiterator_step(Dataiterator *di)
1478 {
1479   Id schema;
1480
1481   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
1482     unsigned int ddpoff = di->ddp - di->vert_ddp;
1483     di->vert_off += ddpoff;
1484     di->vert_len -= ddpoff;
1485     di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1486     di->vert_storestate = di->data->storestate;
1487     if (!di->ddp)
1488       di->state = di_nextkey;
1489   }
1490   for (;;)
1491     {
1492       switch (di->state)
1493         {
1494         case di_enterrepo: di_enterrepo:
1495           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1496             goto di_nextrepo;
1497           if (!(di->flags & SEARCH_THISSOLVID))
1498             {
1499               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1500               goto di_nextsolvable;
1501             }
1502           /* FALLTHROUGH */
1503
1504         case di_entersolvable: di_entersolvable:
1505           if (di->repodataid)
1506             {
1507               di->repodataid = 1;       /* reset repodata iterator */
1508               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)
1509                 {
1510                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1511
1512                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1513                   di->data = 0;
1514                   goto di_entersolvablekey;
1515                 }
1516             }
1517           /* FALLTHROUGH */
1518
1519         case di_enterrepodata: di_enterrepodata:
1520           if (di->repodataid)
1521             {
1522               if (di->repodataid >= di->repo->nrepodata)
1523                 goto di_nextsolvable;
1524               di->data = di->repo->repodata + di->repodataid;
1525             }
1526           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1527             goto di_nextrepodata;
1528           if (!maybe_load_repodata(di->data, di->keyname))
1529             goto di_nextrepodata;
1530           di->dp = solvid2data(di->data, di->solvid, &schema);
1531           if (!di->dp)
1532             goto di_nextrepodata;
1533           if (di->solvid == SOLVID_POS)
1534             di->solvid = di->pool->pos.solvid;
1535           /* reset key iterator */
1536           di->keyp = di->data->schemadata + di->data->schemata[schema];
1537           /* FALLTHROUGH */
1538
1539         case di_enterschema: di_enterschema:
1540           if (di->keyname)
1541             di->dp = dataiterator_find_keyname(di, di->keyname);
1542           if (!di->dp || !*di->keyp)
1543             {
1544               if (di->kv.parent)
1545                 goto di_leavesub;
1546               goto di_nextrepodata;
1547             }
1548           /* FALLTHROUGH */
1549
1550         case di_enterkey: di_enterkey:
1551           di->kv.entry = -1;
1552           di->key = di->data->keys + *di->keyp;
1553           if (!di->dp)
1554             goto di_nextkey;
1555           /* this is get_data() modified to store vert_ data */
1556           if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1557             {
1558               Id off, len;
1559               di->dp = data_read_id(di->dp, &off);
1560               di->dp = data_read_id(di->dp, &len);
1561               di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1562               di->vert_off = off;
1563               di->vert_len = len;
1564               di->vert_storestate = di->data->storestate;
1565             }
1566           else if (di->key->storage == KEY_STORAGE_INCORE)
1567             {
1568               di->ddp = di->dp;
1569               if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1570                 di->dp = data_skip_key(di->data, di->dp, di->key);
1571             }
1572           else
1573             di->ddp = 0;
1574           if (!di->ddp)
1575             goto di_nextkey;
1576           if (di->key->type == REPOKEY_TYPE_DELETED)
1577             goto di_nextkey;
1578           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1579             goto di_enterarray;
1580           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1581             goto di_nextkey;
1582           /* FALLTHROUGH */
1583
1584         case di_nextattr:
1585           di->kv.entry++;
1586           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1587           if (di->kv.eof)
1588             di->state = di_nextkey;
1589           else
1590             di->state = di_nextattr;
1591           break;
1592
1593         case di_nextkey: di_nextkey:
1594           if (!di->keyname && *++di->keyp)
1595             goto di_enterkey;
1596           if (di->kv.parent)
1597             goto di_leavesub;
1598           /* FALLTHROUGH */
1599
1600         case di_nextrepodata: di_nextrepodata:
1601           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1602               goto di_enterrepodata;
1603           /* FALLTHROUGH */
1604
1605         case di_nextsolvable: di_nextsolvable:
1606           if (!(di->flags & SEARCH_THISSOLVID))
1607             {
1608               if (di->solvid < 0)
1609                 di->solvid = di->repo->start;
1610               else
1611                 di->solvid++;
1612               for (; di->solvid < di->repo->end; di->solvid++)
1613                 {
1614                   if (di->pool->solvables[di->solvid].repo == di->repo)
1615                     goto di_entersolvable;
1616                 }
1617             }
1618           /* FALLTHROUGH */
1619
1620         case di_nextrepo: di_nextrepo:
1621           if (di->repoid > 0)
1622             {
1623               di->repoid++;
1624               di->repodataid = 1;
1625               if (di->repoid < di->pool->nrepos)
1626                 {
1627                   di->repo = di->pool->repos[di->repoid];
1628                   goto di_enterrepo;
1629                 }
1630             }
1631         /* FALLTHROUGH */
1632
1633         case di_bye: di_bye:
1634           di->state = di_bye;
1635           return 0;
1636
1637         case di_enterarray: di_enterarray:
1638           if (di->key->name == REPOSITORY_SOLVABLES)
1639             goto di_nextkey;
1640           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1641           di->kv.eof = 0;
1642           di->kv.entry = -1;
1643           /* FALLTHROUGH */
1644
1645         case di_nextarrayelement: di_nextarrayelement:
1646           di->kv.entry++;
1647           if (di->kv.entry)
1648             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1649           if (di->kv.entry == di->kv.num)
1650             {
1651               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1652                 goto di_nextkey;
1653               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1654                 goto di_nextkey;
1655               di->kv.str = (char *)di->ddp;
1656               di->kv.eof = 2;
1657               di->state = di_nextkey;
1658               break;
1659             }
1660           if (di->kv.entry == di->kv.num - 1)
1661             di->kv.eof = 1;
1662           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1663             di->ddp = data_read_id(di->ddp, &di->kv.id);
1664           di->kv.str = (char *)di->ddp;
1665           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1666             goto di_entersub;
1667           if ((di->flags & SEARCH_SUB) != 0)
1668             di->state = di_entersub;
1669           else
1670             di->state = di_nextarrayelement;
1671           break;
1672
1673         case di_entersub: di_entersub:
1674           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1675             goto di_nextarrayelement;   /* sorry, full */
1676           di->parents[di->nparents].kv = di->kv;
1677           di->parents[di->nparents].dp = di->dp;
1678           di->parents[di->nparents].keyp = di->keyp;
1679           di->dp = (unsigned char *)di->kv.str;
1680           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1681           memset(&di->kv, 0, sizeof(di->kv));
1682           di->kv.parent = &di->parents[di->nparents].kv;
1683           di->nparents++;
1684           di->keyname = di->keynames[di->nparents - di->rootlevel];
1685           goto di_enterschema;
1686
1687         case di_leavesub: di_leavesub:
1688           if (di->nparents - 1 < di->rootlevel)
1689             goto di_bye;
1690           di->nparents--;
1691           di->dp = di->parents[di->nparents].dp;
1692           di->kv = di->parents[di->nparents].kv;
1693           di->keyp = di->parents[di->nparents].keyp;
1694           di->key = di->data->keys + *di->keyp;
1695           di->ddp = (unsigned char *)di->kv.str;
1696           di->keyname = di->keynames[di->nparents - di->rootlevel];
1697           goto di_nextarrayelement;
1698
1699         /* special solvable attr handling follows */
1700
1701         case di_nextsolvablekey: di_nextsolvablekey:
1702           if (di->keyname || di->key->name == RPM_RPMDBID)
1703             goto di_enterrepodata;
1704           di->key++;
1705           /* FALLTHROUGH */
1706
1707         case di_entersolvablekey: di_entersolvablekey:
1708           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1709           if (!di->idp || !*di->idp)
1710             goto di_nextsolvablekey;
1711           if (di->kv.eof)
1712             {
1713               /* not an array */
1714               di->kv.id = *di->idp;
1715               di->kv.num = *di->idp;    /* for rpmdbid */
1716               di->kv.num2 = 0;          /* for rpmdbid */
1717               di->kv.entry = 0;
1718               di->state = di_nextsolvablekey;
1719               break;
1720             }
1721           di->kv.entry = -1;
1722           /* FALLTHROUGH */
1723
1724         case di_nextsolvableattr:
1725           di->state = di_nextsolvableattr;
1726           di->kv.id = *di->idp++;
1727           di->kv.entry++;
1728           if (!*di->idp)
1729             {
1730               di->kv.eof = 1;
1731               di->state = di_nextsolvablekey;
1732             }
1733           break;
1734
1735         }
1736
1737       if (di->matcher.match)
1738         {
1739           const char *str;
1740           /* simple pre-check so that we don't need to stringify */
1741           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1742             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1743               continue;
1744           if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
1745             {
1746               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1747                 return 1;
1748               continue;
1749             }
1750           if (!datamatcher_match(&di->matcher, str))
1751             continue;
1752         }
1753       else
1754         {
1755           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1756             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1757         }
1758       /* found something! */
1759       return 1;
1760     }
1761 }
1762
1763 void
1764 dataiterator_entersub(Dataiterator *di)
1765 {
1766   if (di->state == di_nextarrayelement)
1767     di->state = di_entersub;
1768 }
1769
1770 void
1771 dataiterator_setpos(Dataiterator *di)
1772 {
1773   if (di->kv.eof == 2)
1774     {
1775       pool_clear_pos(di->pool);
1776       return;
1777     }
1778   di->pool->pos.solvid = di->solvid;
1779   di->pool->pos.repo = di->repo;
1780   di->pool->pos.repodataid = di->data - di->repo->repodata;
1781   di->pool->pos.schema = di->kv.id;
1782   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1783 }
1784
1785 void
1786 dataiterator_setpos_parent(Dataiterator *di)
1787 {
1788   if (!di->kv.parent || di->kv.parent->eof == 2)
1789     {
1790       pool_clear_pos(di->pool);
1791       return;
1792     }
1793   di->pool->pos.solvid = di->solvid;
1794   di->pool->pos.repo = di->repo;
1795   di->pool->pos.repodataid = di->data - di->repo->repodata;
1796   di->pool->pos.schema = di->kv.parent->id;
1797   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1798 }
1799
1800 /* clones just the position, not the search keys/matcher */
1801 void
1802 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1803 {
1804   di->state = from->state;
1805   di->flags &= ~SEARCH_THISSOLVID;
1806   di->flags |= (from->flags & SEARCH_THISSOLVID);
1807   di->repo = from->repo;
1808   di->data = from->data;
1809   di->dp = from->dp;
1810   di->ddp = from->ddp;
1811   di->idp = from->idp;
1812   di->keyp = from->keyp;
1813   di->key = from->key;
1814   di->kv = from->kv;
1815   di->repodataid = from->repodataid;
1816   di->solvid = from->solvid;
1817   di->repoid = from->repoid;
1818   di->rootlevel = from->rootlevel;
1819   memcpy(di->parents, from->parents, sizeof(from->parents));
1820   di->nparents = from->nparents;
1821   if (di->nparents)
1822     {
1823       int i;
1824       for (i = 1; i < di->nparents; i++)
1825         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1826       di->kv.parent = &di->parents[di->nparents - 1].kv;
1827     }
1828   di->dupstr = 0;
1829   di->dupstrn = 0;
1830   if (from->dupstr && from->dupstr == from->kv.str)
1831     {
1832       di->dupstrn = from->dupstrn;
1833       di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
1834     }
1835 }
1836
1837 void
1838 dataiterator_seek(Dataiterator *di, int whence)
1839 {
1840   if ((whence & DI_SEEK_STAY) != 0)
1841     di->rootlevel = di->nparents;
1842   switch (whence & ~DI_SEEK_STAY)
1843     {
1844     case DI_SEEK_CHILD:
1845       if (di->state != di_nextarrayelement)
1846         break;
1847       if ((whence & DI_SEEK_STAY) != 0)
1848         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1849       di->state = di_entersub;
1850       break;
1851     case DI_SEEK_PARENT:
1852       if (!di->nparents)
1853         {
1854           di->state = di_bye;
1855           break;
1856         }
1857       di->nparents--;
1858       if (di->rootlevel > di->nparents)
1859         di->rootlevel = di->nparents;
1860       di->dp = di->parents[di->nparents].dp;
1861       di->kv = di->parents[di->nparents].kv;
1862       di->keyp = di->parents[di->nparents].keyp;
1863       di->key = di->data->keys + *di->keyp;
1864       di->ddp = (unsigned char *)di->kv.str;
1865       di->keyname = di->keynames[di->nparents - di->rootlevel];
1866       di->state = di_nextarrayelement;
1867       break;
1868     case DI_SEEK_REWIND:
1869       if (!di->nparents)
1870         {
1871           di->state = di_bye;
1872           break;
1873         }
1874       di->dp = (unsigned char *)di->kv.parent->str;
1875       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1876       di->state = di_enterschema;
1877       break;
1878     default:
1879       break;
1880     }
1881 }
1882
1883 void
1884 dataiterator_skip_attribute(Dataiterator *di)
1885 {
1886   if (di->state == di_nextsolvableattr)
1887     di->state = di_nextsolvablekey;
1888   else
1889     di->state = di_nextkey;
1890 }
1891
1892 void
1893 dataiterator_skip_solvable(Dataiterator *di)
1894 {
1895   di->nparents = 0;
1896   di->kv.parent = 0;
1897   di->rootlevel = 0;
1898   di->keyname = di->keynames[0];
1899   di->state = di_nextsolvable;
1900 }
1901
1902 void
1903 dataiterator_skip_repo(Dataiterator *di)
1904 {
1905   di->nparents = 0;
1906   di->kv.parent = 0;
1907   di->rootlevel = 0;
1908   di->keyname = di->keynames[0];
1909   di->state = di_nextrepo;
1910 }
1911
1912 void
1913 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1914 {
1915   di->nparents = 0;
1916   di->kv.parent = 0;
1917   di->rootlevel = 0;
1918   di->keyname = di->keynames[0];
1919   if (solvid == SOLVID_POS)
1920     {
1921       di->repo = di->pool->pos.repo;
1922       if (!di->repo)
1923         {
1924           di->state = di_bye;
1925           return;
1926         }
1927       di->repoid = 0;
1928       di->data = di->repo->repodata + di->pool->pos.repodataid;
1929       di->repodataid = 0;
1930       di->solvid = solvid;
1931       di->state = di_enterrepo;
1932       di->flags |= SEARCH_THISSOLVID;
1933       return;
1934     }
1935   if (solvid > 0)
1936     {
1937       di->repo = di->pool->solvables[solvid].repo;
1938       di->repoid = 0;
1939     }
1940   else if (di->repoid > 0)
1941     {
1942       if (!di->pool->urepos)
1943         {
1944           di->state = di_bye;
1945           return;
1946         }
1947       di->repoid = 1;
1948       di->repo = di->pool->repos[di->repoid];
1949     }
1950   di->repodataid = 1;
1951   di->solvid = solvid;
1952   if (solvid)
1953     di->flags |= SEARCH_THISSOLVID;
1954   di->state = di_enterrepo;
1955 }
1956
1957 void
1958 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1959 {
1960   di->nparents = 0;
1961   di->kv.parent = 0;
1962   di->rootlevel = 0;
1963   di->repo = repo;
1964   di->repoid = 0;       /* 0 means stay at repo */
1965   di->repodataid = 1;
1966   di->solvid = 0;
1967   di->flags &= ~SEARCH_THISSOLVID;
1968   di->state = di_enterrepo;
1969 }
1970
1971 int
1972 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1973 {
1974   const char *str;
1975   if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
1976     return 0;
1977   return ma ? datamatcher_match(ma, str) : 1;
1978 }
1979
1980 void
1981 dataiterator_strdup(Dataiterator *di)
1982 {
1983   int l = -1;
1984
1985   if (!di->kv.str || di->kv.str == di->dupstr)
1986     return;
1987   switch (di->key->type)
1988     {
1989     case_CHKSUM_TYPES:
1990     case REPOKEY_TYPE_DIRSTRARRAY:
1991       if (di->kv.num)   /* was it stringified into tmp space? */
1992         l = strlen(di->kv.str) + 1;
1993       break;
1994     default:
1995       break;
1996     }
1997   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1998     {
1999       switch (di->key->type)
2000         {
2001         case REPOKEY_TYPE_STR:
2002         case REPOKEY_TYPE_DIRSTRARRAY:
2003           l = strlen(di->kv.str) + 1;
2004           break;
2005         case_CHKSUM_TYPES:
2006           l = solv_chksum_len(di->key->type);
2007           break;
2008         case REPOKEY_TYPE_BINARY:
2009           l = di->kv.num;
2010           break;
2011         }
2012     }
2013   if (l >= 0)
2014     {
2015       if (!di->dupstrn || di->dupstrn < l)
2016         {
2017           di->dupstrn = l + 16;
2018           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
2019         }
2020       if (l)
2021         memcpy(di->dupstr, di->kv.str, l);
2022       di->kv.str = di->dupstr;
2023     }
2024 }
2025
2026 /************************************************************************
2027  * data modify functions
2028  */
2029
2030 /* extend repodata so that it includes solvables p */
2031 void
2032 repodata_extend(Repodata *data, Id p)
2033 {
2034   if (data->start == data->end)
2035     data->start = data->end = p;
2036   if (p >= data->end)
2037     {
2038       int old = data->end - data->start;
2039       int new = p - data->end + 1;
2040       if (data->attrs)
2041         {
2042           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
2043           memset(data->attrs + old, 0, new * sizeof(Id *));
2044         }
2045       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
2046       memset(data->incoreoffset + old, 0, new * sizeof(Id));
2047       data->end = p + 1;
2048     }
2049   if (p < data->start)
2050     {
2051       int old = data->end - data->start;
2052       int new = data->start - p;
2053       if (data->attrs)
2054         {
2055           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
2056           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
2057           memset(data->attrs, 0, new * sizeof(Id *));
2058         }
2059       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
2060       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
2061       memset(data->incoreoffset, 0, new * sizeof(Id));
2062       data->start = p;
2063     }
2064 }
2065
2066 /* shrink end of repodata */
2067 void
2068 repodata_shrink(Repodata *data, int end)
2069 {
2070   int i;
2071
2072   if (data->end <= end)
2073     return;
2074   if (data->start >= end)
2075     {
2076       if (data->attrs)
2077         {
2078           for (i = 0; i < data->end - data->start; i++)
2079             solv_free(data->attrs[i]);
2080           data->attrs = solv_free(data->attrs);
2081         }
2082       data->incoreoffset = solv_free(data->incoreoffset);
2083       data->start = data->end = 0;
2084       return;
2085     }
2086   if (data->attrs)
2087     {
2088       for (i = end; i < data->end; i++)
2089         solv_free(data->attrs[i - data->start]);
2090       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2091     }
2092   if (data->incoreoffset)
2093     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2094   data->end = end;
2095 }
2096
2097 /* extend repodata so that it includes solvables from start to start + num - 1 */
2098 void
2099 repodata_extend_block(Repodata *data, Id start, Id num)
2100 {
2101   if (!num)
2102     return;
2103   if (!data->incoreoffset)
2104     {
2105       /* this also means that data->attrs is NULL */
2106       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2107       data->start = start;
2108       data->end = start + num;
2109       return;
2110     }
2111   repodata_extend(data, start);
2112   if (num > 1)
2113     repodata_extend(data, start + num - 1);
2114 }
2115
2116 /**********************************************************************/
2117
2118
2119 #define REPODATA_ATTRS_BLOCK 31
2120 #define REPODATA_ATTRDATA_BLOCK 1023
2121 #define REPODATA_ATTRIDDATA_BLOCK 63
2122 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2123
2124
2125 Id
2126 repodata_new_handle(Repodata *data)
2127 {
2128   if (!data->nxattrs)
2129     {
2130       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2131       data->nxattrs = 2;        /* -1: SOLVID_META */
2132     }
2133   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2134   data->xattrs[data->nxattrs] = 0;
2135   return -(data->nxattrs++);
2136 }
2137
2138 static inline Id **
2139 repodata_get_attrp(Repodata *data, Id handle)
2140 {
2141   if (handle < 0)
2142     {
2143       if (handle == SOLVID_META && !data->xattrs)
2144         {
2145           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2146           data->nxattrs = 2;
2147         }
2148       return data->xattrs - handle;
2149     }
2150   if (handle < data->start || handle >= data->end)
2151     repodata_extend(data, handle);
2152   if (!data->attrs)
2153     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2154   return data->attrs + (handle - data->start);
2155 }
2156
2157 static void
2158 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2159 {
2160   Id *pp;
2161   Id *ap, **app;
2162   int i;
2163
2164   app = repodata_get_attrp(data, handle);
2165   ap = *app;
2166   i = 0;
2167   if (ap)
2168     {
2169       /* Determine equality based on the name only, allows us to change
2170          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2171       for (pp = ap; *pp; pp += 2)
2172         if (data->keys[*pp].name == data->keys[keyid].name)
2173           break;
2174       if (*pp)
2175         {
2176           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2177             {
2178               pp[0] = keyid;
2179               pp[1] = val;
2180             }
2181           return;
2182         }
2183       i = pp - ap;
2184     }
2185   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2186   *app = ap;
2187   pp = ap + i;
2188   *pp++ = keyid;
2189   *pp++ = val;
2190   *pp = 0;
2191 }
2192
2193
2194 static void
2195 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2196 {
2197   Id keyid;
2198
2199   keyid = repodata_key2id(data, key, 1);
2200   repodata_insert_keyid(data, solvid, keyid, val, 1);
2201 }
2202
2203 void
2204 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2205 {
2206   Repokey key;
2207   key.name = keyname;
2208   key.type = REPOKEY_TYPE_ID;
2209   key.size = 0;
2210   key.storage = KEY_STORAGE_INCORE;
2211   repodata_set(data, solvid, &key, id);
2212 }
2213
2214 void
2215 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2216 {
2217   Repokey key;
2218   key.name = keyname;
2219   key.type = REPOKEY_TYPE_NUM;
2220   key.size = 0;
2221   key.storage = KEY_STORAGE_INCORE;
2222   if (num >= 0x80000000)
2223     {
2224       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2225       data->attrnum64data[data->attrnum64datalen] = num;
2226       num = 0x80000000 | data->attrnum64datalen++;
2227     }
2228   repodata_set(data, solvid, &key, (Id)num);
2229 }
2230
2231 void
2232 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2233 {
2234   Repokey key;
2235   Id id;
2236   if (data->localpool)
2237     id = stringpool_str2id(&data->spool, str, 1);
2238   else
2239     id = pool_str2id(data->repo->pool, str, 1);
2240   key.name = keyname;
2241   key.type = REPOKEY_TYPE_ID;
2242   key.size = 0;
2243   key.storage = KEY_STORAGE_INCORE;
2244   repodata_set(data, solvid, &key, id);
2245 }
2246
2247 void
2248 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2249 {
2250   Repokey key;
2251   key.name = keyname;
2252   key.type = REPOKEY_TYPE_CONSTANT;
2253   key.size = constant;
2254   key.storage = KEY_STORAGE_INCORE;
2255   repodata_set(data, solvid, &key, 0);
2256 }
2257
2258 void
2259 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2260 {
2261   Repokey key;
2262   key.name = keyname;
2263   key.type = REPOKEY_TYPE_CONSTANTID;
2264   key.size = id;
2265   key.storage = KEY_STORAGE_INCORE;
2266   repodata_set(data, solvid, &key, 0);
2267 }
2268
2269 void
2270 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2271 {
2272   Repokey key;
2273   key.name = keyname;
2274   key.type = REPOKEY_TYPE_VOID;
2275   key.size = 0;
2276   key.storage = KEY_STORAGE_INCORE;
2277   repodata_set(data, solvid, &key, 0);
2278 }
2279
2280 void
2281 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2282 {
2283   Repokey key;
2284   int l;
2285
2286   l = strlen(str) + 1;
2287   key.name = keyname;
2288   key.type = REPOKEY_TYPE_STR;
2289   key.size = 0;
2290   key.storage = KEY_STORAGE_INCORE;
2291   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2292   memcpy(data->attrdata + data->attrdatalen, str, l);
2293   repodata_set(data, solvid, &key, data->attrdatalen);
2294   data->attrdatalen += l;
2295 }
2296
2297 void
2298 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2299 {
2300   Repokey key;
2301   unsigned char *dp;
2302
2303   if (len < 0)
2304     return;
2305   key.name = keyname;
2306   key.type = REPOKEY_TYPE_BINARY;
2307   key.size = 0;
2308   key.storage = KEY_STORAGE_INCORE;
2309   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2310   dp = data->attrdata + data->attrdatalen;
2311   if (len >= (1 << 14))
2312     {
2313       if (len >= (1 << 28))
2314         *dp++ = (len >> 28) | 128;
2315       if (len >= (1 << 21))
2316         *dp++ = (len >> 21) | 128;
2317       *dp++ = (len >> 14) | 128;
2318     }
2319   if (len >= (1 << 7))
2320     *dp++ = (len >> 7) | 128;
2321   *dp++ = len & 127;
2322   if (len)
2323     memcpy(dp, buf, len);
2324   repodata_set(data, solvid, &key, data->attrdatalen);
2325   data->attrdatalen = dp + len - data->attrdata;
2326 }
2327
2328 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2329  * so that the caller can append entrysize new elements plus the termination zero there */
2330 static void
2331 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2332 {
2333   int oldsize;
2334   Id *ida, *pp, **ppp;
2335
2336   /* check if it is the same as last time, this speeds things up a lot */
2337   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2338     {
2339       /* great! just append the new data */
2340       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2341       data->attriddatalen--;    /* overwrite terminating 0  */
2342       data->lastdatalen += entrysize;
2343       return;
2344     }
2345
2346   ppp = repodata_get_attrp(data, handle);
2347   pp = *ppp;
2348   if (pp)
2349     {
2350       for (; *pp; pp += 2)
2351         if (data->keys[*pp].name == keyname)
2352           break;
2353     }
2354   if (!pp || !*pp || data->keys[*pp].type != keytype)
2355     {
2356       /* not found. allocate new key */
2357       Repokey key;
2358       Id keyid;
2359       key.name = keyname;
2360       key.type = keytype;
2361       key.size = 0;
2362       key.storage = KEY_STORAGE_INCORE;
2363       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2364       keyid = repodata_key2id(data, &key, 1);
2365       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2366       data->lasthandle = handle;
2367       data->lastkey = keyid;
2368       data->lastdatalen = data->attriddatalen + entrysize + 1;
2369       return;
2370     }
2371   oldsize = 0;
2372   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2373     oldsize += entrysize;
2374   if (ida + 1 == data->attriddata + data->attriddatalen)
2375     {
2376       /* this was the last entry, just append it */
2377       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2378       data->attriddatalen--;    /* overwrite terminating 0  */
2379     }
2380   else
2381     {
2382       /* too bad. move to back. */
2383       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2384       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2385       pp[1] = data->attriddatalen;
2386       data->attriddatalen += oldsize;
2387     }
2388   data->lasthandle = handle;
2389   data->lastkey = *pp;
2390   data->lastdatalen = data->attriddatalen + entrysize + 1;
2391 }
2392
2393 void
2394 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2395                       const unsigned char *str)
2396 {
2397   Repokey key;
2398   int l;
2399
2400   if (!(l = solv_chksum_len(type)))
2401     return;
2402   key.name = keyname;
2403   key.type = type;
2404   key.size = 0;
2405   key.storage = KEY_STORAGE_INCORE;
2406   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2407   memcpy(data->attrdata + data->attrdatalen, str, l);
2408   repodata_set(data, solvid, &key, data->attrdatalen);
2409   data->attrdatalen += l;
2410 }
2411
2412 void
2413 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2414                       const char *str)
2415 {
2416   unsigned char buf[64];
2417   int l;
2418
2419   if (!(l = solv_chksum_len(type)))
2420     return;
2421   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2422     return;
2423   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2424 }
2425
2426 const char *
2427 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2428 {
2429   int l;
2430
2431   if (!(l = solv_chksum_len(type)))
2432     return "";
2433   return pool_bin2hex(data->repo->pool, buf, l);
2434 }
2435
2436 /* rpm filenames don't contain the epoch, so strip it */
2437 static inline const char *
2438 evrid2vrstr(Pool *pool, Id evrid)
2439 {
2440   const char *p, *evr = pool_id2str(pool, evrid);
2441   if (!evr)
2442     return evr;
2443   for (p = evr; *p >= '0' && *p <= '9'; p++)
2444     ;
2445   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2446 }
2447
2448 static inline void
2449 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2450 {
2451   Id id;
2452   if (data->localpool)
2453     id = stringpool_strn2id(&data->spool, str, l, 1);
2454   else
2455     id = pool_strn2id(data->repo->pool, str, l, 1);
2456   repodata_set_id(data, solvid, keyname, id);
2457 }
2458
2459 static inline void
2460 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2461 {
2462   if (!str[l])
2463     repodata_set_str(data, solvid, keyname, str);
2464   else
2465     {
2466       char *s = solv_strdup(str);
2467       s[l] = 0;
2468       repodata_set_str(data, solvid, keyname, s);
2469       free(s);
2470     }
2471 }
2472
2473 void
2474 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2475 {
2476   Pool *pool = data->repo->pool;
2477   Solvable *s;
2478   const char *str, *fp;
2479   int l = 0;
2480
2481   if (medianr)
2482     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2483   if (!dir)
2484     {
2485       if ((dir = strrchr(file, '/')) != 0)
2486         {
2487           l = dir - file;
2488           dir = file;
2489           file = dir + l + 1;
2490           if (!l)
2491             l++;
2492         }
2493     }
2494   else
2495     l = strlen(dir);
2496   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2497     {
2498       dir += 2;
2499       l -= 2;
2500     }
2501   if (l == 1 && dir[0] == '.')
2502     l = 0;
2503   s = pool->solvables + solvid;
2504   if (dir && l)
2505     {
2506       str = pool_id2str(pool, s->arch);
2507       if (!strncmp(dir, str, l) && !str[l])
2508         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2509       else
2510         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2511     }
2512   fp = file;
2513   str = pool_id2str(pool, s->name);
2514   l = strlen(str);
2515   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2516     {
2517       fp += l + 1;
2518       str = evrid2vrstr(pool, s->evr);
2519       l = strlen(str);
2520       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2521         {
2522           fp += l + 1;
2523           str = pool_id2str(pool, s->arch);
2524           l = strlen(str);
2525           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2526             {
2527               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2528               return;
2529             }
2530         }
2531     }
2532   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2533 }
2534
2535 /* XXX: medianr is currently not stored */
2536 void
2537 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2538 {
2539   int l = 0;
2540   const char *evr, *suf, *s;
2541
2542   if (!dir)
2543     {
2544       if ((dir = strrchr(file, '/')) != 0)
2545         {
2546           l = dir - file;
2547           dir = file;
2548           file = dir + l + 1;
2549           if (!l)
2550             l++;
2551         }
2552     }
2553   else
2554     l = strlen(dir);
2555   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2556     {
2557       dir += 2;
2558       l -= 2;
2559     }
2560   if (l == 1 && dir[0] == '.')
2561     l = 0;
2562   if (dir && l)
2563     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2564   evr = strchr(file, '-');
2565   if (evr)
2566     {
2567       for (s = evr - 1; s > file; s--)
2568         if (*s == '-')
2569           {
2570             evr = s;
2571             break;
2572           }
2573     }
2574   suf = strrchr(file, '.');
2575   if (suf)
2576     {
2577       for (s = suf - 1; s > file; s--)
2578         if (*s == '.')
2579           {
2580             suf = s;
2581             break;
2582           }
2583       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2584         {
2585           /* We accept one more item as suffix.  */
2586           for (s = suf - 1; s > file; s--)
2587             if (*s == '.')
2588               {
2589                 suf = s;
2590                 break;
2591               }
2592         }
2593     }
2594   if (!evr)
2595     suf = 0;
2596   if (suf && evr && suf < evr)
2597     suf = 0;
2598   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2599   if (evr)
2600     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2601   if (suf)
2602     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2603 }
2604
2605 void
2606 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2607 {
2608   Pool *pool = data->repo->pool;
2609   Solvable *s = pool->solvables + solvid;
2610   const char *p, *sevr, *sarch, *name, *evr;
2611
2612   p = strrchr(sourcepkg, '.');
2613   if (!p || strcmp(p, ".rpm") != 0)
2614     {
2615       if (*sourcepkg)
2616         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2617       return;
2618     }
2619   p--;
2620   while (p > sourcepkg && *p != '.')
2621     p--;
2622   if (*p != '.' || p == sourcepkg)
2623     return;
2624   sarch = p-- + 1;
2625   while (p > sourcepkg && *p != '-')
2626     p--;
2627   if (*p != '-' || p == sourcepkg)
2628     return;
2629   p--;
2630   while (p > sourcepkg && *p != '-')
2631     p--;
2632   if (*p != '-' || p == sourcepkg)
2633     return;
2634   sevr = p + 1;
2635   pool = s->repo->pool;
2636
2637   name = pool_id2str(pool, s->name);
2638   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2639     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2640   else
2641     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2642
2643   evr = evrid2vrstr(pool, s->evr);
2644   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2645     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2646   else
2647     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2648
2649   if (!strcmp(sarch, "src.rpm"))
2650     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2651   else if (!strcmp(sarch, "nosrc.rpm"))
2652     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2653   else
2654     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2655 }
2656
2657 void
2658 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2659 {
2660   Repokey key;
2661   int i;
2662
2663   key.name = keyname;
2664   key.type = REPOKEY_TYPE_IDARRAY;
2665   key.size = 0;
2666   key.storage = KEY_STORAGE_INCORE;
2667   repodata_set(data, solvid, &key, data->attriddatalen);
2668   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2669   for (i = 0; i < q->count; i++)
2670     data->attriddata[data->attriddatalen++] = q->elements[i];
2671   data->attriddata[data->attriddatalen++] = 0;
2672 }
2673
2674 void
2675 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2676 {
2677   assert(dir);
2678 #if 0
2679 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2680 #endif
2681   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2682   data->attriddata[data->attriddatalen++] = dir;
2683   data->attriddata[data->attriddatalen++] = num;
2684   data->attriddata[data->attriddatalen++] = num2;
2685   data->attriddata[data->attriddatalen++] = 0;
2686 }
2687
2688 void
2689 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2690 {
2691   Id stroff;
2692   int l;
2693
2694   assert(dir);
2695   l = strlen(str) + 1;
2696   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2697   memcpy(data->attrdata + data->attrdatalen, str, l);
2698   stroff = data->attrdatalen;
2699   data->attrdatalen += l;
2700
2701 #if 0
2702 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2703 #endif
2704   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2705   data->attriddata[data->attriddatalen++] = dir;
2706   data->attriddata[data->attriddatalen++] = stroff;
2707   data->attriddata[data->attriddatalen++] = 0;
2708 }
2709
2710 void
2711 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2712 {
2713 #if 0
2714 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2715 #endif
2716   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2717   data->attriddata[data->attriddatalen++] = id;
2718   data->attriddata[data->attriddatalen++] = 0;
2719 }
2720
2721 void
2722 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2723                            const char *str)
2724 {
2725   Id id;
2726   if (data->localpool)
2727     id = stringpool_str2id(&data->spool, str, 1);
2728   else
2729     id = pool_str2id(data->repo->pool, str, 1);
2730   repodata_add_idarray(data, solvid, keyname, id);
2731 }
2732
2733 void
2734 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2735 {
2736   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2737   data->attriddata[data->attriddatalen++] = ghandle;
2738   data->attriddata[data->attriddatalen++] = 0;
2739 }
2740
2741 void
2742 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2743 {
2744   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2745   data->attriddata[data->attriddatalen++] = ghandle;
2746   data->attriddata[data->attriddatalen++] = 0;
2747 }
2748
2749 void
2750 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2751 {
2752   Id *pp, *ap, **app;
2753   app = repodata_get_attrp(data, solvid);
2754   ap = *app;
2755   if (!ap)
2756     return;
2757   for (; *ap; ap += 2)
2758     if (data->keys[*ap].name == keyname)
2759       break;
2760   if (!*ap)
2761     return;
2762   pp = ap;
2763   ap += 2;
2764   for (; *ap; ap += 2)
2765     {
2766       if (data->keys[*ap].name == keyname)
2767         continue;
2768       *pp++ = ap[0];
2769       *pp++ = ap[1];
2770     }
2771   *pp = 0;
2772 }
2773
2774 /* XXX: does not work correctly, needs fix in iterators! */
2775 void
2776 repodata_unset(Repodata *data, Id solvid, Id keyname)
2777 {
2778   Repokey key;
2779   key.name = keyname;
2780   key.type = REPOKEY_TYPE_DELETED;
2781   key.size = 0;
2782   key.storage = KEY_STORAGE_INCORE;
2783   repodata_set(data, solvid, &key, 0);
2784 }
2785
2786 /* add all (uninternalized) attrs from src to dest */
2787 void
2788 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2789 {
2790   Id *keyp;
2791   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2792     return;
2793   for (; *keyp; keyp += 2)
2794     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2795 }
2796
2797 /* add some (uninternalized) attrs from src to dest */
2798 void
2799 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2800 {
2801   Id *keyp;
2802   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2803     return;
2804   for (; *keyp; keyp += 2)
2805     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2806       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2807 }
2808
2809 /* swap (uninternalized) attrs from src and dest */
2810 void
2811 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2812 {
2813   Id *tmpattrs;
2814   if (!data->attrs || dest == src)
2815     return;
2816   if (dest < data->start || dest >= data->end)
2817     repodata_extend(data, dest);
2818   if (src < data->start || src >= data->end)
2819     repodata_extend(data, src);
2820   tmpattrs = data->attrs[dest - data->start];
2821   data->attrs[dest - data->start] = data->attrs[src - data->start];
2822   data->attrs[src - data->start] = tmpattrs;
2823 }
2824
2825
2826 /**********************************************************************/
2827
2828 /* TODO: unify with repo_write and repo_solv! */
2829
2830 #define EXTDATA_BLOCK 1023
2831
2832 struct extdata {
2833   unsigned char *buf;
2834   int len;
2835 };
2836
2837 static void
2838 data_addid(struct extdata *xd, Id sx)
2839 {
2840   unsigned int x = (unsigned int)sx;
2841   unsigned char *dp;
2842
2843   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2844   dp = xd->buf + xd->len;
2845
2846   if (x >= (1 << 14))
2847     {
2848       if (x >= (1 << 28))
2849         *dp++ = (x >> 28) | 128;
2850       if (x >= (1 << 21))
2851         *dp++ = (x >> 21) | 128;
2852       *dp++ = (x >> 14) | 128;
2853     }
2854   if (x >= (1 << 7))
2855     *dp++ = (x >> 7) | 128;
2856   *dp++ = x & 127;
2857   xd->len = dp - xd->buf;
2858 }
2859
2860 static void
2861 data_addid64(struct extdata *xd, unsigned long long x)
2862 {
2863   if (x >= 0x100000000)
2864     {
2865       if ((x >> 35) != 0)
2866         {
2867           data_addid(xd, (Id)(x >> 35));
2868           xd->buf[xd->len - 1] |= 128;
2869         }
2870       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2871       xd->buf[xd->len - 5] = (x >> 28) | 128;
2872     }
2873   else
2874     data_addid(xd, (Id)x);
2875 }
2876
2877 static void
2878 data_addideof(struct extdata *xd, Id sx, int eof)
2879 {
2880   unsigned int x = (unsigned int)sx;
2881   unsigned char *dp;
2882
2883   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2884   dp = xd->buf + xd->len;
2885
2886   if (x >= (1 << 13))
2887     {
2888       if (x >= (1 << 27))
2889         *dp++ = (x >> 27) | 128;
2890       if (x >= (1 << 20))
2891         *dp++ = (x >> 20) | 128;
2892       *dp++ = (x >> 13) | 128;
2893     }
2894   if (x >= (1 << 6))
2895     *dp++ = (x >> 6) | 128;
2896   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2897   xd->len = dp - xd->buf;
2898 }
2899
2900 static void
2901 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2902 {
2903   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2904   memcpy(xd->buf + xd->len, blob, len);
2905   xd->len += len;
2906 }
2907
2908 /*********************************/
2909
2910 /* this is to reduct memory usage when internalizing oversized repos */
2911 static void
2912 compact_attrdata(Repodata *data, int entry, int nentry)
2913 {
2914   int i;
2915   unsigned int attrdatastart = data->attrdatalen;
2916   unsigned int attriddatastart = data->attriddatalen;
2917   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
2918     return;
2919   for (i = entry; i < nentry; i++)
2920     {
2921       Id v, *attrs = data->attrs[i];
2922       if (!attrs)
2923         continue;
2924       for (; *attrs; attrs += 2)
2925         {
2926           switch (data->keys[*attrs].type)
2927             {
2928             case REPOKEY_TYPE_STR:
2929             case REPOKEY_TYPE_BINARY:
2930             case_CHKSUM_TYPES:
2931               if ((unsigned int)attrs[1] < attrdatastart)
2932                  attrdatastart = attrs[1];
2933               break;
2934             case REPOKEY_TYPE_DIRSTRARRAY:
2935               for (v = attrs[1]; data->attriddata[v] ; v += 2)
2936                 if (data->attriddata[v + 1] < attrdatastart)
2937                   attrdatastart = data->attriddata[v + 1];
2938               /* FALLTHROUGH */
2939             case REPOKEY_TYPE_IDARRAY:
2940             case REPOKEY_TYPE_DIRNUMNUMARRAY:
2941               if ((unsigned int)attrs[1] < attriddatastart)
2942                 attriddatastart = attrs[1];
2943               break;
2944             case REPOKEY_TYPE_FIXARRAY:
2945             case REPOKEY_TYPE_FLEXARRAY:
2946               return;
2947             default:
2948               break;
2949             }
2950         }
2951     }
2952 #if 0
2953   printf("compact_attrdata %d %d\n", entry, nentry);
2954   printf("attrdatastart: %d\n", attrdatastart);
2955   printf("attriddatastart: %d\n", attriddatastart);
2956 #endif
2957   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
2958     return;
2959   for (i = entry; i < nentry; i++)
2960     {
2961       Id v, *attrs = data->attrs[i];
2962       if (!attrs)
2963         continue;
2964       for (; *attrs; attrs += 2)
2965         {
2966           switch (data->keys[*attrs].type)
2967             {
2968             case REPOKEY_TYPE_STR:
2969             case REPOKEY_TYPE_BINARY:
2970             case_CHKSUM_TYPES:
2971               attrs[1] -= attrdatastart;
2972               break;
2973             case REPOKEY_TYPE_DIRSTRARRAY:
2974               for (v = attrs[1]; data->attriddata[v] ; v += 2)
2975                 data->attriddata[v + 1] -= attrdatastart;
2976               /* FALLTHROUGH */
2977             case REPOKEY_TYPE_IDARRAY:
2978             case REPOKEY_TYPE_DIRNUMNUMARRAY:
2979               attrs[1] -= attriddatastart;
2980               break;
2981             default:
2982               break;
2983             }
2984         }
2985     }
2986   if (attrdatastart)
2987     {
2988       data->attrdatalen -= attrdatastart;
2989       memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
2990       data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
2991     }
2992   if (attriddatastart)
2993     {
2994       data->attriddatalen -= attriddatastart;
2995       memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
2996       data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2997     }
2998 }
2999
3000 /* internalalize some key into incore/vincore data */
3001
3002 static void
3003 repodata_serialize_key(Repodata *data, struct extdata *newincore,
3004                        struct extdata *newvincore,
3005                        Id *schema,
3006                        Repokey *key, Id val)
3007 {
3008   Id *ida;
3009   struct extdata *xd;
3010   unsigned int oldvincorelen = 0;
3011   Id schemaid, *sp;
3012
3013   xd = newincore;
3014   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3015     {
3016       xd = newvincore;
3017       oldvincorelen = xd->len;
3018     }
3019   switch (key->type)
3020     {
3021     case REPOKEY_TYPE_VOID:
3022     case REPOKEY_TYPE_CONSTANT:
3023     case REPOKEY_TYPE_CONSTANTID:
3024       break;
3025     case REPOKEY_TYPE_STR:
3026       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
3027       break;
3028     case REPOKEY_TYPE_MD5:
3029       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
3030       break;
3031     case REPOKEY_TYPE_SHA1:
3032       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
3033       break;
3034     case REPOKEY_TYPE_SHA224:
3035       data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
3036       break;
3037     case REPOKEY_TYPE_SHA256:
3038       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
3039       break;
3040     case REPOKEY_TYPE_SHA384:
3041       data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
3042       break;
3043     case REPOKEY_TYPE_SHA512:
3044       data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
3045       break;
3046     case REPOKEY_TYPE_NUM:
3047       if (val & 0x80000000)
3048         {
3049           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
3050           break;
3051         }
3052       /* FALLTHROUGH */
3053     case REPOKEY_TYPE_ID:
3054     case REPOKEY_TYPE_DIR:
3055       data_addid(xd, val);
3056       break;
3057     case REPOKEY_TYPE_BINARY:
3058       {
3059         Id len;
3060         unsigned char *dp = data_read_id(data->attrdata + val, &len);
3061         dp += (unsigned int)len;
3062         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
3063       }
3064       break;
3065     case REPOKEY_TYPE_IDARRAY:
3066       for (ida = data->attriddata + val; *ida; ida++)
3067         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
3068       break;
3069     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3070       for (ida = data->attriddata + val; *ida; ida += 3)
3071         {
3072           data_addid(xd, ida[0]);
3073           data_addid(xd, ida[1]);
3074           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
3075         }
3076       break;
3077     case REPOKEY_TYPE_DIRSTRARRAY:
3078       for (ida = data->attriddata + val; *ida; ida += 2)
3079         {
3080           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
3081           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
3082         }
3083       break;
3084     case REPOKEY_TYPE_FIXARRAY:
3085       {
3086         int num = 0;
3087         schemaid = 0;
3088         for (ida = data->attriddata + val; *ida; ida++)
3089           {
3090             Id *kp;
3091             sp = schema;
3092             kp = data->xattrs[-*ida];
3093             if (!kp)
3094               continue;
3095             num++;
3096             for (;*kp; kp += 2)
3097               *sp++ = *kp;
3098             *sp = 0;
3099             if (!schemaid)
3100               schemaid = repodata_schema2id(data, schema, 1);
3101             else if (schemaid != repodata_schema2id(data, schema, 0))
3102               {
3103                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
3104                 exit(1);
3105               }
3106           }
3107         if (!num)
3108           break;
3109         data_addid(xd, num);
3110         data_addid(xd, schemaid);
3111         for (ida = data->attriddata + val; *ida; ida++)
3112           {
3113             Id *kp = data->xattrs[-*ida];
3114             if (!kp)
3115               continue;
3116             for (;*kp; kp += 2)
3117               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3118           }
3119         break;
3120       }
3121     case REPOKEY_TYPE_FLEXARRAY:
3122       {
3123         int num = 0;
3124         for (ida = data->attriddata + val; *ida; ida++)
3125           num++;
3126         data_addid(xd, num);
3127         for (ida = data->attriddata + val; *ida; ida++)
3128           {
3129             Id *kp = data->xattrs[-*ida];
3130             if (!kp)
3131               {
3132                 data_addid(xd, 0);      /* XXX */
3133                 continue;
3134               }
3135             sp = schema;
3136             for (;*kp; kp += 2)
3137               *sp++ = *kp;
3138             *sp = 0;
3139             schemaid = repodata_schema2id(data, schema, 1);
3140             data_addid(xd, schemaid);
3141             kp = data->xattrs[-*ida];
3142             for (;*kp; kp += 2)
3143               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3144           }
3145         break;
3146       }
3147     default:
3148       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
3149       exit(1);
3150     }
3151   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3152     {
3153       /* put offset/len in incore */
3154       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
3155       oldvincorelen = xd->len - oldvincorelen;
3156       data_addid(newincore, oldvincorelen);
3157     }
3158 }
3159
3160 void
3161 repodata_internalize(Repodata *data)
3162 {
3163   Repokey *key, solvkey;
3164   Id entry, nentry;
3165   Id schemaid, keyid, *schema, *sp, oldschema, *keyp, *seen;
3166   int schemaidx;
3167   unsigned char *dp, *ndp;
3168   int newschema, oldcount;
3169   struct extdata newincore;
3170   struct extdata newvincore;
3171   Id solvkeyid;
3172
3173   if (!data->attrs && !data->xattrs)
3174     return;
3175
3176 #if 0
3177   printf("repodata_internalize %d\n", data->repodataid);
3178   printf("  attr data: %d K\n", data->attrdatalen / 1024);
3179   printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3180 #endif
3181   newvincore.buf = data->vincore;
3182   newvincore.len = data->vincorelen;
3183
3184   /* find the solvables key, create if needed */
3185   memset(&solvkey, 0, sizeof(solvkey));
3186   solvkey.name = REPOSITORY_SOLVABLES;
3187   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3188   solvkey.size = 0;
3189   solvkey.storage = KEY_STORAGE_INCORE;
3190   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3191
3192   schema = solv_malloc2(data->nkeys, sizeof(Id));
3193   seen = solv_malloc2(data->nkeys, sizeof(Id));
3194
3195   /* Merge the data already existing (in data->schemata, ->incoredata and
3196      friends) with the new attributes in data->attrs[].  */
3197   nentry = data->end - data->start;
3198   memset(&newincore, 0, sizeof(newincore));
3199   data_addid(&newincore, 0);    /* start data at offset 1 */
3200
3201   data->mainschema = 0;
3202   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3203
3204   /* join entry data */
3205   /* we start with the meta data, entry -1 */
3206   for (entry = -1; entry < nentry; entry++)
3207     {
3208       memset(seen, 0, data->nkeys * sizeof(Id));
3209       oldschema = 0;
3210       dp = data->incoredata;
3211       if (dp)
3212         {
3213           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3214           dp = data_read_id(dp, &oldschema);
3215         }
3216 #if 0
3217 fprintf(stderr, "oldschema %d\n", oldschema);
3218 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3219 fprintf(stderr, "schemadata %p\n", data->schemadata);
3220 #endif
3221       /* seen: -1: old data  0: skipped  >0: id + 1 */
3222       newschema = 0;
3223       oldcount = 0;
3224       sp = schema;
3225       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3226         {
3227           if (seen[*keyp])
3228             {
3229               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3230               exit(1);
3231             }
3232           seen[*keyp] = -1;
3233           *sp++ = *keyp;
3234           oldcount++;
3235         }
3236       if (entry >= 0)
3237         keyp = data->attrs ? data->attrs[entry] : 0;
3238       else
3239         {
3240           /* strip solvables key */
3241           *sp = 0;
3242           for (sp = keyp = schema; *sp; sp++)
3243             if (*sp != solvkeyid)
3244               *keyp++ = *sp;
3245             else
3246               oldcount--;
3247           sp = keyp;
3248           seen[solvkeyid] = 0;
3249           keyp = data->xattrs ? data->xattrs[1] : 0;
3250         }
3251       if (keyp)
3252         for (; *keyp; keyp += 2)
3253           {
3254             if (!seen[*keyp])
3255               {
3256                 newschema = 1;
3257                 *sp++ = *keyp;
3258               }
3259             seen[*keyp] = keyp[1] + 1;
3260           }
3261       if (entry < 0 && data->end != data->start)
3262         {
3263           *sp++ = solvkeyid;
3264           newschema = 1;
3265         }
3266       *sp = 0;
3267       if (newschema)
3268         /* Ideally we'd like to sort the new schema here, to ensure
3269            schema equality independend of the ordering.  We can't do that
3270            yet.  For once see below (old ids need to come before new ids).
3271            An additional difficulty is that we also need to move
3272            the values with the keys.  */
3273         schemaid = repodata_schema2id(data, schema, 1);
3274       else
3275         schemaid = oldschema;
3276
3277
3278       /* Now create data blob.  We walk through the (possibly new) schema
3279          and either copy over old data, or insert the new.  */
3280       /* XXX Here we rely on the fact that the (new) schema has the form
3281          o1 o2 o3 o4 ... | n1 n2 n3 ...
3282          (oX being the old keyids (possibly overwritten), and nX being
3283           the new keyids).  This rules out sorting the keyids in order
3284          to ensure a small schema count.  */
3285       if (entry >= 0)
3286         data->incoreoffset[entry] = newincore.len;
3287       data_addid(&newincore, schemaid);
3288       if (entry == -1)
3289         {
3290           data->mainschema = schemaid;
3291           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3292         }
3293       /* we don't use a pointer to the schemadata here as repodata_serialize_key
3294        * may call repodata_schema2id() which might realloc our schemadata */
3295       for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
3296         {
3297           if (entry == -1)
3298             data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
3299           if (keyid == solvkeyid)
3300             {
3301               /* add flexarray entry count */
3302               data_addid(&newincore, data->end - data->start);
3303               break;
3304             }
3305           key = data->keys + keyid;
3306 #if 0
3307           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));
3308 #endif
3309           ndp = dp;
3310           if (oldcount)
3311             {
3312               /* Skip the data associated with this old key.  */
3313               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3314                 {
3315                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3316                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3317                 }
3318               else if (key->storage == KEY_STORAGE_INCORE)
3319                 ndp = data_skip_key(data, dp, key);
3320               oldcount--;
3321             }
3322           if (seen[keyid] == -1)
3323             {
3324               /* If this key was an old one _and_ was not overwritten with
3325                  a different value copy over the old value (we skipped it
3326                  above).  */
3327               if (dp != ndp)
3328                 data_addblob(&newincore, dp, ndp - dp);
3329               seen[keyid] = 0;
3330             }
3331           else if (seen[keyid])
3332             {
3333               /* Otherwise we have a new value.  Parse it into the internal form.  */
3334               repodata_serialize_key(data, &newincore, &newvincore, schema, key, seen[keyid] - 1);
3335             }
3336           dp = ndp;
3337         }
3338       if (entry >= 0 && data->attrs)
3339         {
3340           if (data->attrs[entry])
3341             data->attrs[entry] = solv_free(data->attrs[entry]);
3342           if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
3343             {
3344               compact_attrdata(data, entry + 1, nentry);        /* try to free some memory */
3345 #if 0
3346               printf("  attr data: %d K\n", data->attrdatalen / 1024);
3347               printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3348               printf("  incore data: %d K\n", newincore.len / 1024);
3349               printf("  sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
3350               /* malloc_stats(); */
3351 #endif
3352             }
3353         }
3354     }
3355   /* free all xattrs */
3356   for (entry = 0; entry < data->nxattrs; entry++)
3357     if (data->xattrs[entry])
3358       solv_free(data->xattrs[entry]);
3359   data->xattrs = solv_free(data->xattrs);
3360   data->nxattrs = 0;
3361
3362   data->lasthandle = 0;
3363   data->lastkey = 0;
3364   data->lastdatalen = 0;
3365   solv_free(schema);
3366   solv_free(seen);
3367   repodata_free_schemahash(data);
3368
3369   solv_free(data->incoredata);
3370   data->incoredata = newincore.buf;
3371   data->incoredatalen = newincore.len;
3372   data->incoredatafree = 0;
3373
3374   solv_free(data->vincore);
3375   data->vincore = newvincore.buf;
3376   data->vincorelen = newvincore.len;
3377
3378   data->attrs = solv_free(data->attrs);
3379   data->attrdata = solv_free(data->attrdata);
3380   data->attriddata = solv_free(data->attriddata);
3381   data->attrnum64data = solv_free(data->attrnum64data);
3382   data->attrdatalen = 0;
3383   data->attriddatalen = 0;
3384   data->attrnum64datalen = 0;
3385 #if 0
3386   printf("repodata_internalize %d done\n", data->repodataid);
3387   printf("  incore data: %d K\n", data->incoredatalen / 1024);
3388 #endif
3389 }
3390
3391 void
3392 repodata_disable_paging(Repodata *data)
3393 {
3394   if (maybe_load_repodata(data, 0))
3395     {
3396       repopagestore_disable_paging(&data->store);
3397       data->storestate++;
3398     }
3399 }
3400
3401 static void
3402 repodata_load_stub(Repodata *data)
3403 {
3404   Repo *repo = data->repo;
3405   Pool *pool = repo->pool;
3406   int r, i;
3407   struct _Pool_tmpspace oldtmpspace;
3408   Datapos oldpos;
3409
3410   if (!pool->loadcallback)
3411     {
3412       data->state = REPODATA_ERROR;
3413       return;
3414     }
3415   data->state = REPODATA_LOADING;
3416
3417   /* save tmp space and pos */
3418   oldtmpspace = pool->tmpspace;
3419   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3420   oldpos = pool->pos;
3421
3422   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3423
3424   /* restore tmp space and pos */
3425   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3426     solv_free(pool->tmpspace.buf[i]);
3427   pool->tmpspace = oldtmpspace;
3428   if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
3429     memset(&oldpos, 0, sizeof(oldpos));
3430   pool->pos = oldpos;
3431
3432   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3433 }
3434
3435 static inline void
3436 repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
3437 {
3438   Repokey xkey;
3439
3440   xkey.name = keyname;
3441   xkey.type = keytype;
3442   xkey.storage = KEY_STORAGE_INCORE;
3443   xkey.size = 0;
3444   repodata_key2id(data, &xkey, 1);
3445 }
3446
3447 static Repodata *
3448 repodata_add_stub(Repodata **datap)
3449 {
3450   Repodata *data = *datap;
3451   Repo *repo = data->repo;
3452   Id repodataid = data - repo->repodata;
3453   Repodata *sdata = repo_add_repodata(repo, 0);
3454   data = repo->repodata + repodataid;
3455   if (data->end > data->start)
3456     repodata_extend_block(sdata, data->start, data->end - data->start);
3457   sdata->state = REPODATA_STUB;
3458   sdata->loadcallback = repodata_load_stub;
3459   *datap = data;
3460   return sdata;
3461 }
3462
3463 Repodata *
3464 repodata_create_stubs(Repodata *data)
3465 {
3466   Repo *repo = data->repo;
3467   Pool *pool = repo->pool;
3468   Repodata *sdata;
3469   int *stubdataids;
3470   Dataiterator di;
3471   Id xkeyname = 0;
3472   int i, cnt = 0;
3473
3474   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3475   while (dataiterator_step(&di))
3476     if (di.data == data)
3477       cnt++;
3478   dataiterator_free(&di);
3479   if (!cnt)
3480     return data;
3481   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3482   for (i = 0; i < cnt; i++)
3483     {
3484       sdata = repodata_add_stub(&data);
3485       stubdataids[i] = sdata - repo->repodata;
3486     }
3487   i = 0;
3488   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3489   sdata = 0;
3490   while (dataiterator_step(&di))
3491     {
3492       if (di.data != data)
3493         continue;
3494       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3495         {
3496           dataiterator_entersub(&di);
3497           sdata = repo->repodata + stubdataids[i++];
3498           xkeyname = 0;
3499           continue;
3500         }
3501       switch (di.key->type)
3502         {
3503         case REPOKEY_TYPE_ID:
3504           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3505           break;
3506         case REPOKEY_TYPE_CONSTANTID:
3507           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3508           break;
3509         case REPOKEY_TYPE_STR:
3510           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3511           break;
3512         case REPOKEY_TYPE_VOID:
3513           repodata_set_void(sdata, SOLVID_META, di.key->name);
3514           break;
3515         case REPOKEY_TYPE_NUM:
3516           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3517           break;
3518         case_CHKSUM_TYPES:
3519           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3520           break;
3521         case REPOKEY_TYPE_IDARRAY:
3522           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3523           if (di.key->name == REPOSITORY_KEYS)
3524             {
3525               if (!xkeyname)
3526                 {
3527                   if (!di.kv.eof)
3528                     xkeyname = di.kv.id;
3529                 }
3530               else
3531                 {
3532                   repodata_add_stubkey(sdata, xkeyname, di.kv.id);
3533                   xkeyname = 0;
3534                 }
3535             }
3536           break;
3537         default:
3538           break;
3539         }
3540     }
3541   dataiterator_free(&di);
3542   for (i = 0; i < cnt; i++)
3543     repodata_internalize(repo->repodata + stubdataids[i]);
3544   solv_free(stubdataids);
3545   return data;
3546 }
3547
3548 unsigned int
3549 repodata_memused(Repodata *data)
3550 {
3551   return data->incoredatalen + data->vincorelen;
3552 }
3553