c35da23abd1823d421fd74276a87eb94acda3b9f
[platform/upstream/libsolv.git] / src / repodata.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repodata.c
10  *
11  * Manage data coming from one repository
12  *
13  * a repository can contain multiple repodata entries, consisting of
14  * different sets of keys and different sets of solvables
15  */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <fnmatch.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <regex.h>
26
27 #include "repo.h"
28 #include "pool.h"
29 #include "poolid_private.h"
30 #include "util.h"
31 #include "hash.h"
32 #include "chksum.h"
33
34 #include "repopack.h"
35 #include "repopage.h"
36
37 #define REPODATA_BLOCK 255
38
39 static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
40
41 void
42 repodata_initdata(Repodata *data, Repo *repo, int localpool)
43 {
44   memset(data, 0, sizeof (*data));
45   data->repodataid = data - repo->repodata;
46   data->repo = repo;
47   data->localpool = localpool;
48   if (localpool)
49     stringpool_init_empty(&data->spool);
50   /* dirpool_init(&data->dirpool);      just zeros out again */
51   data->keys = solv_calloc(1, sizeof(Repokey));
52   data->nkeys = 1;
53   data->schemata = solv_calloc(1, sizeof(Id));
54   data->schemadata = solv_calloc(1, sizeof(Id));
55   data->nschemata = 1;
56   data->schemadatalen = 1;
57   repopagestore_init(&data->store);
58 }
59
60 void
61 repodata_freedata(Repodata *data)
62 {
63   int i;
64
65   solv_free(data->keys);
66
67   solv_free(data->schemata);
68   solv_free(data->schemadata);
69   solv_free(data->schematahash);
70
71   stringpool_free(&data->spool);
72   dirpool_free(&data->dirpool);
73
74   solv_free(data->mainschemaoffsets);
75   solv_free(data->incoredata);
76   solv_free(data->incoreoffset);
77   solv_free(data->verticaloffset);
78
79   repopagestore_free(&data->store);
80
81   solv_free(data->vincore);
82
83   if (data->attrs)
84     for (i = 0; i < data->end - data->start; i++)
85       solv_free(data->attrs[i]);
86   solv_free(data->attrs);
87   if (data->xattrs)
88     for (i = 0; i < data->nxattrs; i++)
89       solv_free(data->xattrs[i]);
90   solv_free(data->xattrs);
91
92   solv_free(data->attrdata);
93   solv_free(data->attriddata);
94   solv_free(data->attrnum64data);
95
96   solv_free(data->dircache);
97 }
98
99 void
100 repodata_free(Repodata *data)
101 {
102   Repo *repo = data->repo;
103   int i = data - repo->repodata;
104   if (i == 0)
105     return;
106   repodata_freedata(data);
107   if (i < repo->nrepodata - 1)
108     {
109       /* whoa! this changes the repodataids! */
110       memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
111       for (; i < repo->nrepodata - 1; i++)
112         repo->repodata[i].repodataid = i;
113     }
114   repo->nrepodata--;
115   if (repo->nrepodata == 1)
116     {
117       repo->repodata = solv_free(repo->repodata);
118       repo->nrepodata = 0;
119     }
120 }
121
122 void
123 repodata_empty(Repodata *data, int localpool)
124 {
125   void (*loadcallback)(Repodata *) = data->loadcallback;
126   int state = data->state;
127   repodata_freedata(data);
128   repodata_initdata(data, data->repo, localpool);
129   data->state = state;
130   data->loadcallback = loadcallback;
131 }
132
133
134 /***************************************************************
135  * key pool management
136  */
137
138 /* this is not so time critical that we need a hash, so we do a simple
139  * linear search */
140 Id
141 repodata_key2id(Repodata *data, Repokey *key, int create)
142 {
143   Id keyid;
144
145   for (keyid = 1; keyid < data->nkeys; keyid++)
146     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
147       {
148         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
149           continue;
150         break;
151       }
152   if (keyid == data->nkeys)
153     {
154       if (!create)
155         return 0;
156       /* allocate new key */
157       data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
158       data->keys[data->nkeys++] = *key;
159       if (data->verticaloffset)
160         {
161           data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
162           data->verticaloffset[data->nkeys - 1] = 0;
163         }
164       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
165     }
166   return keyid;
167 }
168
169
170 /***************************************************************
171  * schema pool management
172  */
173
174 #define SCHEMATA_BLOCK 31
175 #define SCHEMATADATA_BLOCK 255
176
177 Id
178 repodata_schema2id(Repodata *data, Id *schema, int create)
179 {
180   int h, len, i;
181   Id *sp, cid;
182   Id *schematahash;
183
184   if (!*schema)
185     return 0;   /* XXX: allow empty schema? */
186   if ((schematahash = data->schematahash) == 0)
187     {
188       data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
189       for (i = 1; i < data->nschemata; i++)
190         {
191           for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
192             h = h * 7 + *sp++;
193           h &= 255;
194           schematahash[h] = i;
195         }
196       data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
197       data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
198     }
199
200   for (sp = schema, len = 0, h = 0; *sp; len++)
201     h = h * 7 + *sp++;
202   h &= 255;
203   len++;
204
205   cid = schematahash[h];
206   if (cid)
207     {
208       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
209         return cid;
210       /* cache conflict, do a slow search */
211       for (cid = 1; cid < data->nschemata; cid++)
212         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
213           return cid;
214     }
215   /* a new one */
216   if (!create)
217     return 0;
218   data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
219   data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
220   /* add schema */
221   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
222   data->schemata[data->nschemata] = data->schemadatalen;
223   data->schemadatalen += len;
224   schematahash[h] = data->nschemata;
225 #if 0
226 fprintf(stderr, "schema2id: new schema\n");
227 #endif
228   return data->nschemata++;
229 }
230
231 void
232 repodata_free_schemahash(Repodata *data)
233 {
234   data->schematahash = solv_free(data->schematahash);
235   /* shrink arrays */
236   data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
237   data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
238 }
239
240
241 /***************************************************************
242  * dir pool management
243  */
244
245 #ifndef HAVE_STRCHRNUL
246 static inline const char *strchrnul(const char *str, char x)
247 {
248   const char *p = strchr(str, x);
249   return p ? p : str + strlen(str);
250 }
251 #endif
252
253 #define DIRCACHE_SIZE 41        /* < 1k */
254
255 #ifdef DIRCACHE_SIZE
256 struct dircache {
257   Id ids[DIRCACHE_SIZE];
258   char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
259 };
260 #endif
261
262 Id
263 repodata_str2dir(Repodata *data, const char *dir, int create)
264 {
265   Id id, parent;
266 #ifdef DIRCACHE_SIZE
267   const char *dirs;
268 #endif
269   const char *dire;
270
271   parent = 0;
272   if (!*dir)
273     return 0;
274   while (*dir == '/' && dir[1] == '/')
275     dir++;
276   if (*dir == '/' && !dir[1])
277     {
278       if (data->dirpool.ndirs)
279         return 1;
280       return dirpool_add_dir(&data->dirpool, 0, 1, create);
281     }
282 #ifdef DIRCACHE_SIZE
283   dirs = dir;
284   if (data->dircache)
285     {
286       int l;
287       struct dircache *dircache = data->dircache;
288       l = strlen(dir);
289       while (l > 0)
290         {
291           if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
292             {
293               parent = dircache->ids[l];
294               dir += l;
295               if (!*dir)
296                 return parent;
297               while (*dir == '/')
298                 dir++;
299               break;
300             }
301           while (--l)
302             if (dir[l] == '/')
303               break;
304         }
305     }
306 #endif
307   while (*dir)
308     {
309       dire = strchrnul(dir, '/');
310       if (data->localpool)
311         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
312       else
313         id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
314       if (!id)
315         return 0;
316       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
317       if (!parent)
318         return 0;
319 #ifdef DIRCACHE_SIZE
320       if (!data->dircache)
321         data->dircache = solv_calloc(1, sizeof(struct dircache));
322       if (data->dircache)
323         {
324           int l = dire - dirs;
325           if (l < DIRCACHE_SIZE)
326             {
327               data->dircache->ids[l] = parent;
328               memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
329             }
330         }
331 #endif
332       if (!*dire)
333         break;
334       dir = dire + 1;
335       while (*dir == '/')
336         dir++;
337     }
338   return parent;
339 }
340
341 void
342 repodata_free_dircache(Repodata *data)
343 {
344   data->dircache = solv_free(data->dircache);
345 }
346
347 const char *
348 repodata_dir2str(Repodata *data, Id did, const char *suf)
349 {
350   Pool *pool = data->repo->pool;
351   int l = 0;
352   Id parent, comp;
353   const char *comps;
354   char *p;
355
356   if (!did)
357     return suf ? suf : "";
358   parent = did;
359   while (parent)
360     {
361       comp = dirpool_compid(&data->dirpool, parent);
362       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
363       l += strlen(comps);
364       parent = dirpool_parent(&data->dirpool, parent);
365       if (parent)
366         l++;
367     }
368   if (suf)
369     l += strlen(suf) + 1;
370   p = pool_alloctmpspace(pool, l + 1) + l;
371   *p = 0;
372   if (suf)
373     {
374       p -= strlen(suf);
375       strcpy(p, suf);
376       *--p = '/';
377     }
378   parent = did;
379   while (parent)
380     {
381       comp = dirpool_compid(&data->dirpool, parent);
382       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
383       l = strlen(comps);
384       p -= l;
385       strncpy(p, comps, l);
386       parent = dirpool_parent(&data->dirpool, parent);
387       if (parent)
388         *--p = '/';
389     }
390   return p;
391 }
392
393
394 /***************************************************************
395  * data management
396  */
397
398 static inline unsigned char *
399 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
400 {
401   Id *keyp = data->schemadata + data->schemata[schema];
402   for (; *keyp; keyp++)
403     dp = data_skip_key(data, dp, data->keys + *keyp);
404   return dp;
405 }
406
407 static unsigned char *
408 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
409 {
410   int nentries, schema;
411   switch(key->type)
412     {
413     case REPOKEY_TYPE_FIXARRAY:
414       dp = data_read_id(dp, &nentries);
415       if (!nentries)
416         return dp;
417       dp = data_read_id(dp, &schema);
418       while (nentries--)
419         dp = data_skip_schema(data, dp, schema);
420       return dp;
421     case REPOKEY_TYPE_FLEXARRAY:
422       dp = data_read_id(dp, &nentries);
423       while (nentries--)
424         {
425           dp = data_read_id(dp, &schema);
426           dp = data_skip_schema(data, dp, schema);
427         }
428       return dp;
429     default:
430       if (key->storage == KEY_STORAGE_INCORE)
431         dp = data_skip(dp, key->type);
432       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
433         {
434           dp = data_skip(dp, REPOKEY_TYPE_ID);
435           dp = data_skip(dp, REPOKEY_TYPE_ID);
436         }
437       return dp;
438     }
439 }
440
441 static unsigned char *
442 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
443 {
444   Id k;
445
446   if (!keyid)
447     return 0;
448   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
449     {
450       int i;
451       for (i = 0; (k = *keyp++) != 0; i++)
452         if (k == keyid)
453           return data->incoredata + data->mainschemaoffsets[i];
454       return 0;
455     }
456   while ((k = *keyp++) != 0)
457     {
458       if (k == keyid)
459         return dp;
460       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
461         {
462           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
463           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
464           continue;
465         }
466       if (data->keys[k].storage != KEY_STORAGE_INCORE)
467         continue;
468       dp = data_skip_key(data, dp, data->keys + k);
469     }
470   return 0;
471 }
472
473 static unsigned char *
474 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
475 {
476   unsigned char *dp;
477   if (len <= 0)
478     return 0;
479   if (off >= data->lastverticaloffset)
480     {
481       off -= data->lastverticaloffset;
482       if (off + len > data->vincorelen)
483         return 0;
484       return data->vincore + off;
485     }
486   if (off + len > key->size)
487     return 0;
488   /* we now have the offset, go into vertical */
489   off += data->verticaloffset[key - data->keys];
490   /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
491   dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
492   data->storestate++;
493   if (dp)
494     dp += off % REPOPAGE_BLOBSIZE;
495   return dp;
496 }
497
498 static inline unsigned char *
499 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
500 {
501   unsigned char *dp = *dpp;
502
503   if (!dp)
504     return 0;
505   if (key->storage == KEY_STORAGE_INCORE)
506     {
507       if (advance)
508         *dpp = data_skip_key(data, dp, key);
509       return dp;
510     }
511   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
512     {
513       Id off, len;
514       dp = data_read_id(dp, &off);
515       dp = data_read_id(dp, &len);
516       if (advance)
517         *dpp = dp;
518       return get_vertical_data(data, key, off, len);
519     }
520   return 0;
521 }
522
523 static int
524 load_repodata(Repodata *data)
525 {
526   if (data->loadcallback)
527     {
528       data->loadcallback(data);
529       if (data->state == REPODATA_AVAILABLE)
530         return 1;
531     }
532   data->state = REPODATA_ERROR;
533   return 0;
534 }
535
536 static inline int
537 maybe_load_repodata(Repodata *data, Id keyname)
538 {
539   if (keyname && !repodata_precheck_keyname(data, keyname))
540     return 0;   /* do not bother... */
541   switch(data->state)
542     {
543     case REPODATA_STUB:
544       if (keyname)
545         {
546           int i;
547           for (i = 1; i < data->nkeys; i++)
548             if (keyname == data->keys[i].name)
549               break;
550           if (i == data->nkeys)
551             return 0;
552         }
553       return load_repodata(data);
554     case REPODATA_ERROR:
555       return 0;
556     case REPODATA_AVAILABLE:
557     case REPODATA_LOADING:
558       return 1;
559     default:
560       data->state = REPODATA_ERROR;
561       return 0;
562     }
563 }
564
565 static inline unsigned char *
566 solvid2data(Repodata *data, Id solvid, Id *schemap)
567 {
568   unsigned char *dp = data->incoredata;
569   if (!dp)
570     return 0;
571   if (solvid == SOLVID_META)
572     dp += 1;    /* offset of "meta" solvable */
573   else if (solvid == SOLVID_POS)
574     {
575       Pool *pool = data->repo->pool;
576       if (data->repo != pool->pos.repo)
577         return 0;
578       if (data != data->repo->repodata + pool->pos.repodataid)
579         return 0;
580       dp += pool->pos.dp;
581       if (pool->pos.dp != 1)
582         {
583           *schemap = pool->pos.schema;
584           return dp;
585         }
586     }
587   else
588     {
589       if (solvid < data->start || solvid >= data->end)
590         return 0;
591       dp += data->incoreoffset[solvid - data->start];
592     }
593   return data_read_id(dp, schemap);
594 }
595
596 /************************************************************************
597  * data lookup
598  */
599
600 static unsigned char *
601 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
602 {
603   unsigned char *dp;
604   Id schema, *keyp, *kp;
605   Repokey *key;
606
607   if (!maybe_load_repodata(data, keyname))
608     return 0;
609   dp = solvid2data(data, solvid, &schema);
610   if (!dp)
611     return 0;
612   keyp = data->schemadata + data->schemata[schema];
613   for (kp = keyp; *kp; kp++)
614     if (data->keys[*kp].name == keyname)
615       break;
616   if (!*kp)
617     return 0;
618   *keypp = key = data->keys + *kp;
619   if (key->type == REPOKEY_TYPE_DELETED)
620     return 0;
621   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
622     return dp;  /* no need to forward... */
623   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
624     return 0;   /* get_data will not work, no need to forward */
625   dp = forward_to_key(data, *kp, keyp, dp);
626   if (!dp)
627     return 0;
628   return get_data(data, key, &dp, 0);
629 }
630
631 Id
632 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
633 {
634   Id schema, *keyp, *kp;
635   if (!maybe_load_repodata(data, keyname))
636     return 0;
637   if (!solvid2data(data, solvid, &schema))
638     return 0;
639   keyp = data->schemadata + data->schemata[schema];
640   for (kp = keyp; *kp; kp++)
641     if (data->keys[*kp].name == keyname)
642       return data->keys[*kp].type;
643   return 0;
644 }
645
646 Id
647 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
648 {
649   unsigned char *dp;
650   Repokey *key;
651   Id id;
652
653   dp = find_key_data(data, solvid, keyname, &key);
654   if (!dp)
655     return 0;
656   if (key->type == REPOKEY_TYPE_CONSTANTID)
657     return key->size;
658   if (key->type != REPOKEY_TYPE_ID)
659     return 0;
660   dp = data_read_id(dp, &id);
661   return id;
662 }
663
664 const char *
665 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
666 {
667   unsigned char *dp;
668   Repokey *key;
669   Id id;
670
671   dp = find_key_data(data, solvid, keyname, &key);
672   if (!dp)
673     return 0;
674   if (key->type == REPOKEY_TYPE_STR)
675     return (const char *)dp;
676   if (key->type == REPOKEY_TYPE_CONSTANTID)
677     id = key->size;
678   else if (key->type == REPOKEY_TYPE_ID)
679     dp = data_read_id(dp, &id);
680   else
681     return 0;
682   if (data->localpool)
683     return stringpool_id2str(&data->spool, id);
684   return pool_id2str(data->repo->pool, id);
685 }
686
687 int
688 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
689 {
690   unsigned char *dp;
691   Repokey *key;
692   unsigned int high, low;
693
694   *value = 0;
695   dp = find_key_data(data, solvid, keyname, &key);
696   if (!dp)
697     return 0;
698   switch (key->type)
699     {
700     case REPOKEY_TYPE_NUM:
701       data_read_num64(dp, &low, &high);
702       *value = (unsigned long long)high << 32 | low;
703       return 1;
704     case REPOKEY_TYPE_U32:
705       data_read_u32(dp, &low);
706       *value = low;
707       return 1;
708     case REPOKEY_TYPE_CONSTANT:
709       *value = key->size;
710       return 1;
711     default:
712       return 0;
713     }
714 }
715
716 int
717 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
718 {
719   Id schema;
720   Id *keyp;
721   unsigned char *dp;
722
723   if (!maybe_load_repodata(data, keyname))
724     return 0;
725   dp = solvid2data(data, solvid, &schema);
726   if (!dp)
727     return 0;
728   /* can't use find_key_data as we need to test the type */
729   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
730     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
731       return 1;
732   return 0;
733 }
734
735 const unsigned char *
736 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
737 {
738   unsigned char *dp;
739   Repokey *key;
740
741   dp = find_key_data(data, solvid, keyname, &key);
742   if (!dp)
743     return 0;
744   switch (key->type)
745     {
746     case_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       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2106       data->start = start;
2107       data->end = start + num;
2108       return;
2109     }
2110   repodata_extend(data, start);
2111   if (num > 1)
2112     repodata_extend(data, start + num - 1);
2113 }
2114
2115 /**********************************************************************/
2116
2117
2118 #define REPODATA_ATTRS_BLOCK 31
2119 #define REPODATA_ATTRDATA_BLOCK 1023
2120 #define REPODATA_ATTRIDDATA_BLOCK 63
2121 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2122
2123
2124 Id
2125 repodata_new_handle(Repodata *data)
2126 {
2127   if (!data->nxattrs)
2128     {
2129       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2130       data->nxattrs = 2;        /* -1: SOLVID_META */
2131     }
2132   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2133   data->xattrs[data->nxattrs] = 0;
2134   return -(data->nxattrs++);
2135 }
2136
2137 static inline Id **
2138 repodata_get_attrp(Repodata *data, Id handle)
2139 {
2140   if (handle < 0)
2141     {
2142       if (handle == SOLVID_META && !data->xattrs)
2143         {
2144           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2145           data->nxattrs = 2;
2146         }
2147       return data->xattrs - handle;
2148     }
2149   if (handle < data->start || handle >= data->end)
2150     repodata_extend(data, handle);
2151   if (!data->attrs)
2152     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2153   return data->attrs + (handle - data->start);
2154 }
2155
2156 static void
2157 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2158 {
2159   Id *pp;
2160   Id *ap, **app;
2161   int i;
2162
2163   app = repodata_get_attrp(data, handle);
2164   ap = *app;
2165   i = 0;
2166   if (ap)
2167     {
2168       /* Determine equality based on the name only, allows us to change
2169          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2170       for (pp = ap; *pp; pp += 2)
2171         if (data->keys[*pp].name == data->keys[keyid].name)
2172           break;
2173       if (*pp)
2174         {
2175           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2176             {
2177               pp[0] = keyid;
2178               pp[1] = val;
2179             }
2180           return;
2181         }
2182       i = pp - ap;
2183     }
2184   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2185   *app = ap;
2186   pp = ap + i;
2187   *pp++ = keyid;
2188   *pp++ = val;
2189   *pp = 0;
2190 }
2191
2192
2193 static void
2194 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2195 {
2196   Id keyid;
2197
2198   keyid = repodata_key2id(data, key, 1);
2199   repodata_insert_keyid(data, solvid, keyid, val, 1);
2200 }
2201
2202 void
2203 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2204 {
2205   Repokey key;
2206   key.name = keyname;
2207   key.type = REPOKEY_TYPE_ID;
2208   key.size = 0;
2209   key.storage = KEY_STORAGE_INCORE;
2210   repodata_set(data, solvid, &key, id);
2211 }
2212
2213 void
2214 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2215 {
2216   Repokey key;
2217   key.name = keyname;
2218   key.type = REPOKEY_TYPE_NUM;
2219   key.size = 0;
2220   key.storage = KEY_STORAGE_INCORE;
2221   if (num >= 0x80000000)
2222     {
2223       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2224       data->attrnum64data[data->attrnum64datalen] = num;
2225       num = 0x80000000 | data->attrnum64datalen++;
2226     }
2227   repodata_set(data, solvid, &key, (Id)num);
2228 }
2229
2230 void
2231 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2232 {
2233   Repokey key;
2234   Id id;
2235   if (data->localpool)
2236     id = stringpool_str2id(&data->spool, str, 1);
2237   else
2238     id = pool_str2id(data->repo->pool, str, 1);
2239   key.name = keyname;
2240   key.type = REPOKEY_TYPE_ID;
2241   key.size = 0;
2242   key.storage = KEY_STORAGE_INCORE;
2243   repodata_set(data, solvid, &key, id);
2244 }
2245
2246 void
2247 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2248 {
2249   Repokey key;
2250   key.name = keyname;
2251   key.type = REPOKEY_TYPE_CONSTANT;
2252   key.size = constant;
2253   key.storage = KEY_STORAGE_INCORE;
2254   repodata_set(data, solvid, &key, 0);
2255 }
2256
2257 void
2258 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2259 {
2260   Repokey key;
2261   key.name = keyname;
2262   key.type = REPOKEY_TYPE_CONSTANTID;
2263   key.size = id;
2264   key.storage = KEY_STORAGE_INCORE;
2265   repodata_set(data, solvid, &key, 0);
2266 }
2267
2268 void
2269 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2270 {
2271   Repokey key;
2272   key.name = keyname;
2273   key.type = REPOKEY_TYPE_VOID;
2274   key.size = 0;
2275   key.storage = KEY_STORAGE_INCORE;
2276   repodata_set(data, solvid, &key, 0);
2277 }
2278
2279 void
2280 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2281 {
2282   Repokey key;
2283   int l;
2284
2285   l = strlen(str) + 1;
2286   key.name = keyname;
2287   key.type = REPOKEY_TYPE_STR;
2288   key.size = 0;
2289   key.storage = KEY_STORAGE_INCORE;
2290   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2291   memcpy(data->attrdata + data->attrdatalen, str, l);
2292   repodata_set(data, solvid, &key, data->attrdatalen);
2293   data->attrdatalen += l;
2294 }
2295
2296 void
2297 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2298 {
2299   Repokey key;
2300   unsigned char *dp;
2301
2302   if (len < 0)
2303     return;
2304   key.name = keyname;
2305   key.type = REPOKEY_TYPE_BINARY;
2306   key.size = 0;
2307   key.storage = KEY_STORAGE_INCORE;
2308   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2309   dp = data->attrdata + data->attrdatalen;
2310   if (len >= (1 << 14))
2311     {
2312       if (len >= (1 << 28))
2313         *dp++ = (len >> 28) | 128;
2314       if (len >= (1 << 21))
2315         *dp++ = (len >> 21) | 128;
2316       *dp++ = (len >> 14) | 128;
2317     }
2318   if (len >= (1 << 7))
2319     *dp++ = (len >> 7) | 128;
2320   *dp++ = len & 127;
2321   if (len)
2322     memcpy(dp, buf, len);
2323   repodata_set(data, solvid, &key, data->attrdatalen);
2324   data->attrdatalen = dp + len - data->attrdata;
2325 }
2326
2327 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2328  * so that the caller can append entrysize new elements plus the termination zero there */
2329 static void
2330 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2331 {
2332   int oldsize;
2333   Id *ida, *pp, **ppp;
2334
2335   /* check if it is the same as last time, this speeds things up a lot */
2336   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2337     {
2338       /* great! just append the new data */
2339       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2340       data->attriddatalen--;    /* overwrite terminating 0  */
2341       data->lastdatalen += entrysize;
2342       return;
2343     }
2344
2345   ppp = repodata_get_attrp(data, handle);
2346   pp = *ppp;
2347   if (pp)
2348     {
2349       for (; *pp; pp += 2)
2350         if (data->keys[*pp].name == keyname)
2351           break;
2352     }
2353   if (!pp || !*pp || data->keys[*pp].type != keytype)
2354     {
2355       /* not found. allocate new key */
2356       Repokey key;
2357       Id keyid;
2358       key.name = keyname;
2359       key.type = keytype;
2360       key.size = 0;
2361       key.storage = KEY_STORAGE_INCORE;
2362       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2363       keyid = repodata_key2id(data, &key, 1);
2364       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2365       data->lasthandle = handle;
2366       data->lastkey = keyid;
2367       data->lastdatalen = data->attriddatalen + entrysize + 1;
2368       return;
2369     }
2370   oldsize = 0;
2371   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2372     oldsize += entrysize;
2373   if (ida + 1 == data->attriddata + data->attriddatalen)
2374     {
2375       /* this was the last entry, just append it */
2376       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2377       data->attriddatalen--;    /* overwrite terminating 0  */
2378     }
2379   else
2380     {
2381       /* too bad. move to back. */
2382       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2383       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2384       pp[1] = data->attriddatalen;
2385       data->attriddatalen += oldsize;
2386     }
2387   data->lasthandle = handle;
2388   data->lastkey = *pp;
2389   data->lastdatalen = data->attriddatalen + entrysize + 1;
2390 }
2391
2392 void
2393 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2394                       const unsigned char *str)
2395 {
2396   Repokey key;
2397   int l;
2398
2399   if (!(l = solv_chksum_len(type)))
2400     return;
2401   key.name = keyname;
2402   key.type = type;
2403   key.size = 0;
2404   key.storage = KEY_STORAGE_INCORE;
2405   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2406   memcpy(data->attrdata + data->attrdatalen, str, l);
2407   repodata_set(data, solvid, &key, data->attrdatalen);
2408   data->attrdatalen += l;
2409 }
2410
2411 void
2412 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2413                       const char *str)
2414 {
2415   unsigned char buf[64];
2416   int l;
2417
2418   if (!(l = solv_chksum_len(type)))
2419     return;
2420   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2421     return;
2422   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2423 }
2424
2425 const char *
2426 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2427 {
2428   int l;
2429
2430   if (!(l = solv_chksum_len(type)))
2431     return "";
2432   return pool_bin2hex(data->repo->pool, buf, l);
2433 }
2434
2435 /* rpm filenames don't contain the epoch, so strip it */
2436 static inline const char *
2437 evrid2vrstr(Pool *pool, Id evrid)
2438 {
2439   const char *p, *evr = pool_id2str(pool, evrid);
2440   if (!evr)
2441     return evr;
2442   for (p = evr; *p >= '0' && *p <= '9'; p++)
2443     ;
2444   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2445 }
2446
2447 static inline void
2448 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2449 {
2450   Id id;
2451   if (data->localpool)
2452     id = stringpool_strn2id(&data->spool, str, l, 1);
2453   else
2454     id = pool_strn2id(data->repo->pool, str, l, 1);
2455   repodata_set_id(data, solvid, keyname, id);
2456 }
2457
2458 static inline void
2459 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2460 {
2461   if (!str[l])
2462     repodata_set_str(data, solvid, keyname, str);
2463   else
2464     {
2465       char *s = solv_strdup(str);
2466       s[l] = 0;
2467       repodata_set_str(data, solvid, keyname, s);
2468       free(s);
2469     }
2470 }
2471
2472 void
2473 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2474 {
2475   Pool *pool = data->repo->pool;
2476   Solvable *s;
2477   const char *str, *fp;
2478   int l = 0;
2479
2480   if (medianr)
2481     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2482   if (!dir)
2483     {
2484       if ((dir = strrchr(file, '/')) != 0)
2485         {
2486           l = dir - file;
2487           dir = file;
2488           file = dir + l + 1;
2489           if (!l)
2490             l++;
2491         }
2492     }
2493   else
2494     l = strlen(dir);
2495   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2496     {
2497       dir += 2;
2498       l -= 2;
2499     }
2500   if (l == 1 && dir[0] == '.')
2501     l = 0;
2502   s = pool->solvables + solvid;
2503   if (dir && l)
2504     {
2505       str = pool_id2str(pool, s->arch);
2506       if (!strncmp(dir, str, l) && !str[l])
2507         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2508       else
2509         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2510     }
2511   fp = file;
2512   str = pool_id2str(pool, s->name);
2513   l = strlen(str);
2514   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2515     {
2516       fp += l + 1;
2517       str = evrid2vrstr(pool, s->evr);
2518       l = strlen(str);
2519       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2520         {
2521           fp += l + 1;
2522           str = pool_id2str(pool, s->arch);
2523           l = strlen(str);
2524           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2525             {
2526               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2527               return;
2528             }
2529         }
2530     }
2531   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2532 }
2533
2534 /* XXX: medianr is currently not stored */
2535 void
2536 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2537 {
2538   int l = 0;
2539   const char *evr, *suf, *s;
2540
2541   if (!dir)
2542     {
2543       if ((dir = strrchr(file, '/')) != 0)
2544         {
2545           l = dir - file;
2546           dir = file;
2547           file = dir + l + 1;
2548           if (!l)
2549             l++;
2550         }
2551     }
2552   else
2553     l = strlen(dir);
2554   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2555     {
2556       dir += 2;
2557       l -= 2;
2558     }
2559   if (l == 1 && dir[0] == '.')
2560     l = 0;
2561   if (dir && l)
2562     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2563   evr = strchr(file, '-');
2564   if (evr)
2565     {
2566       for (s = evr - 1; s > file; s--)
2567         if (*s == '-')
2568           {
2569             evr = s;
2570             break;
2571           }
2572     }
2573   suf = strrchr(file, '.');
2574   if (suf)
2575     {
2576       for (s = suf - 1; s > file; s--)
2577         if (*s == '.')
2578           {
2579             suf = s;
2580             break;
2581           }
2582       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2583         {
2584           /* We accept one more item as suffix.  */
2585           for (s = suf - 1; s > file; s--)
2586             if (*s == '.')
2587               {
2588                 suf = s;
2589                 break;
2590               }
2591         }
2592     }
2593   if (!evr)
2594     suf = 0;
2595   if (suf && evr && suf < evr)
2596     suf = 0;
2597   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2598   if (evr)
2599     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2600   if (suf)
2601     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2602 }
2603
2604 void
2605 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2606 {
2607   Pool *pool = data->repo->pool;
2608   Solvable *s = pool->solvables + solvid;
2609   const char *p, *sevr, *sarch, *name, *evr;
2610
2611   p = strrchr(sourcepkg, '.');
2612   if (!p || strcmp(p, ".rpm") != 0)
2613     {
2614       if (*sourcepkg)
2615         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2616       return;
2617     }
2618   p--;
2619   while (p > sourcepkg && *p != '.')
2620     p--;
2621   if (*p != '.' || p == sourcepkg)
2622     return;
2623   sarch = p-- + 1;
2624   while (p > sourcepkg && *p != '-')
2625     p--;
2626   if (*p != '-' || p == sourcepkg)
2627     return;
2628   p--;
2629   while (p > sourcepkg && *p != '-')
2630     p--;
2631   if (*p != '-' || p == sourcepkg)
2632     return;
2633   sevr = p + 1;
2634   pool = s->repo->pool;
2635
2636   name = pool_id2str(pool, s->name);
2637   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2638     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2639   else
2640     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2641
2642   evr = evrid2vrstr(pool, s->evr);
2643   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2644     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2645   else
2646     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2647
2648   if (!strcmp(sarch, "src.rpm"))
2649     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2650   else if (!strcmp(sarch, "nosrc.rpm"))
2651     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2652   else
2653     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2654 }
2655
2656 void
2657 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2658 {
2659   Repokey key;
2660   int i;
2661
2662   key.name = keyname;
2663   key.type = REPOKEY_TYPE_IDARRAY;
2664   key.size = 0;
2665   key.storage = KEY_STORAGE_INCORE;
2666   repodata_set(data, solvid, &key, data->attriddatalen);
2667   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2668   for (i = 0; i < q->count; i++)
2669     data->attriddata[data->attriddatalen++] = q->elements[i];
2670   data->attriddata[data->attriddatalen++] = 0;
2671 }
2672
2673 void
2674 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2675 {
2676   assert(dir);
2677 #if 0
2678 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2679 #endif
2680   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2681   data->attriddata[data->attriddatalen++] = dir;
2682   data->attriddata[data->attriddatalen++] = num;
2683   data->attriddata[data->attriddatalen++] = num2;
2684   data->attriddata[data->attriddatalen++] = 0;
2685 }
2686
2687 void
2688 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2689 {
2690   Id stroff;
2691   int l;
2692
2693   assert(dir);
2694   l = strlen(str) + 1;
2695   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2696   memcpy(data->attrdata + data->attrdatalen, str, l);
2697   stroff = data->attrdatalen;
2698   data->attrdatalen += l;
2699
2700 #if 0
2701 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2702 #endif
2703   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2704   data->attriddata[data->attriddatalen++] = dir;
2705   data->attriddata[data->attriddatalen++] = stroff;
2706   data->attriddata[data->attriddatalen++] = 0;
2707 }
2708
2709 void
2710 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2711 {
2712 #if 0
2713 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2714 #endif
2715   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2716   data->attriddata[data->attriddatalen++] = id;
2717   data->attriddata[data->attriddatalen++] = 0;
2718 }
2719
2720 void
2721 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2722                            const char *str)
2723 {
2724   Id id;
2725   if (data->localpool)
2726     id = stringpool_str2id(&data->spool, str, 1);
2727   else
2728     id = pool_str2id(data->repo->pool, str, 1);
2729   repodata_add_idarray(data, solvid, keyname, id);
2730 }
2731
2732 void
2733 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2734 {
2735   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2736   data->attriddata[data->attriddatalen++] = ghandle;
2737   data->attriddata[data->attriddatalen++] = 0;
2738 }
2739
2740 void
2741 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2742 {
2743   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2744   data->attriddata[data->attriddatalen++] = ghandle;
2745   data->attriddata[data->attriddatalen++] = 0;
2746 }
2747
2748 void
2749 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2750 {
2751   Id *pp, *ap, **app;
2752   app = repodata_get_attrp(data, solvid);
2753   ap = *app;
2754   if (!ap)
2755     return;
2756   for (; *ap; ap += 2)
2757     if (data->keys[*ap].name == keyname)
2758       break;
2759   if (!*ap)
2760     return;
2761   pp = ap;
2762   ap += 2;
2763   for (; *ap; ap += 2)
2764     {
2765       if (data->keys[*ap].name == keyname)
2766         continue;
2767       *pp++ = ap[0];
2768       *pp++ = ap[1];
2769     }
2770   *pp = 0;
2771 }
2772
2773 /* XXX: does not work correctly, needs fix in iterators! */
2774 void
2775 repodata_unset(Repodata *data, Id solvid, Id keyname)
2776 {
2777   Repokey key;
2778   key.name = keyname;
2779   key.type = REPOKEY_TYPE_DELETED;
2780   key.size = 0;
2781   key.storage = KEY_STORAGE_INCORE;
2782   repodata_set(data, solvid, &key, 0);
2783 }
2784
2785 /* add all (uninternalized) attrs from src to dest */
2786 void
2787 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2788 {
2789   Id *keyp;
2790   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2791     return;
2792   for (; *keyp; keyp += 2)
2793     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2794 }
2795
2796 /* add some (uninternalized) attrs from src to dest */
2797 void
2798 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2799 {
2800   Id *keyp;
2801   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2802     return;
2803   for (; *keyp; keyp += 2)
2804     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2805       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2806 }
2807
2808 /* swap (uninternalized) attrs from src and dest */
2809 void
2810 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2811 {
2812   Id *tmpattrs;
2813   if (!data->attrs || dest == src)
2814     return;
2815   tmpattrs = data->attrs[dest - data->start];
2816   data->attrs[dest - data->start] = data->attrs[src - data->start];
2817   data->attrs[src - data->start] = tmpattrs;
2818 }
2819
2820
2821 /**********************************************************************/
2822
2823 /* TODO: unify with repo_write and repo_solv! */
2824
2825 #define EXTDATA_BLOCK 1023
2826
2827 struct extdata {
2828   unsigned char *buf;
2829   int len;
2830 };
2831
2832 static void
2833 data_addid(struct extdata *xd, Id sx)
2834 {
2835   unsigned int x = (unsigned int)sx;
2836   unsigned char *dp;
2837
2838   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2839   dp = xd->buf + xd->len;
2840
2841   if (x >= (1 << 14))
2842     {
2843       if (x >= (1 << 28))
2844         *dp++ = (x >> 28) | 128;
2845       if (x >= (1 << 21))
2846         *dp++ = (x >> 21) | 128;
2847       *dp++ = (x >> 14) | 128;
2848     }
2849   if (x >= (1 << 7))
2850     *dp++ = (x >> 7) | 128;
2851   *dp++ = x & 127;
2852   xd->len = dp - xd->buf;
2853 }
2854
2855 static void
2856 data_addid64(struct extdata *xd, unsigned long long x)
2857 {
2858   if (x >= 0x100000000)
2859     {
2860       if ((x >> 35) != 0)
2861         {
2862           data_addid(xd, (Id)(x >> 35));
2863           xd->buf[xd->len - 1] |= 128;
2864         }
2865       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2866       xd->buf[xd->len - 5] = (x >> 28) | 128;
2867     }
2868   else
2869     data_addid(xd, (Id)x);
2870 }
2871
2872 static void
2873 data_addideof(struct extdata *xd, Id sx, int eof)
2874 {
2875   unsigned int x = (unsigned int)sx;
2876   unsigned char *dp;
2877
2878   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2879   dp = xd->buf + xd->len;
2880
2881   if (x >= (1 << 13))
2882     {
2883       if (x >= (1 << 27))
2884         *dp++ = (x >> 27) | 128;
2885       if (x >= (1 << 20))
2886         *dp++ = (x >> 20) | 128;
2887       *dp++ = (x >> 13) | 128;
2888     }
2889   if (x >= (1 << 6))
2890     *dp++ = (x >> 6) | 128;
2891   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2892   xd->len = dp - xd->buf;
2893 }
2894
2895 static void
2896 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2897 {
2898   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2899   memcpy(xd->buf + xd->len, blob, len);
2900   xd->len += len;
2901 }
2902
2903 /*********************************/
2904
2905 /* this is to reduct memory usage when internalizing oversized repos */
2906 static void
2907 compact_attrdata(Repodata *data, int entry, int nentry)
2908 {
2909   int i;
2910   unsigned int attrdatastart = data->attrdatalen;
2911   unsigned int attriddatastart = data->attriddatalen;
2912   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
2913     return;
2914   for (i = entry; i < nentry; i++)
2915     {
2916       Id v, *attrs = data->attrs[i];
2917       if (!attrs)
2918         continue;
2919       for (; *attrs; attrs += 2)
2920         {
2921           switch (data->keys[*attrs].type)
2922             {
2923             case REPOKEY_TYPE_STR:
2924             case REPOKEY_TYPE_BINARY:
2925             case_CHKSUM_TYPES:
2926               if (attrs[1] < attrdatastart)
2927                  attrdatastart = attrs[1];
2928               break;
2929             case REPOKEY_TYPE_DIRSTRARRAY:
2930               for (v = attrs[1]; data->attriddata[v] ; v += 2)
2931                 if (data->attriddata[v + 1] < attrdatastart)
2932                   attrdatastart = data->attriddata[v + 1];
2933               /* FALLTHROUGH */
2934             case REPOKEY_TYPE_IDARRAY:
2935             case REPOKEY_TYPE_DIRNUMNUMARRAY:
2936               if (attrs[1] < attriddatastart)
2937                 attriddatastart = attrs[1];
2938               break;
2939             case REPOKEY_TYPE_FIXARRAY:
2940             case REPOKEY_TYPE_FLEXARRAY:
2941               return;
2942             default:
2943               break;
2944             }
2945         }
2946     }
2947 #if 0
2948   printf("compact_attrdata %d %d\n", entry, nentry);
2949   printf("attrdatastart: %d\n", attrdatastart);
2950   printf("attriddatastart: %d\n", attriddatastart);
2951 #endif
2952   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
2953     return;
2954   for (i = entry; i < nentry; i++)
2955     {
2956       Id v, *attrs = data->attrs[i];
2957       if (!attrs)
2958         continue;
2959       for (; *attrs; attrs += 2)
2960         {
2961           switch (data->keys[*attrs].type)
2962             {
2963             case REPOKEY_TYPE_STR:
2964             case REPOKEY_TYPE_BINARY:
2965             case_CHKSUM_TYPES:
2966               attrs[1] -= attrdatastart;
2967               break;
2968             case REPOKEY_TYPE_DIRSTRARRAY:
2969               for (v = attrs[1]; data->attriddata[v] ; v += 2)
2970                 data->attriddata[v + 1] -= attrdatastart;
2971               /* FALLTHROUGH */
2972             case REPOKEY_TYPE_IDARRAY:
2973             case REPOKEY_TYPE_DIRNUMNUMARRAY:
2974               attrs[1] -= attriddatastart;
2975               break;
2976             default:
2977               break;
2978             }
2979         }
2980     }
2981   if (attrdatastart)
2982     {
2983       data->attrdatalen -= attrdatastart;
2984       memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
2985       data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
2986     }
2987   if (attriddatastart)
2988     {
2989       data->attriddatalen -= attriddatastart;
2990       memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
2991       data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2992     }
2993 }
2994
2995 /* internalalize some key into incore/vincore data */
2996
2997 static void
2998 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2999                        struct extdata *newvincore,
3000                        Id *schema,
3001                        Repokey *key, Id val)
3002 {
3003   Id *ida;
3004   struct extdata *xd;
3005   unsigned int oldvincorelen = 0;
3006   Id schemaid, *sp;
3007
3008   xd = newincore;
3009   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3010     {
3011       xd = newvincore;
3012       oldvincorelen = xd->len;
3013     }
3014   switch (key->type)
3015     {
3016     case REPOKEY_TYPE_VOID:
3017     case REPOKEY_TYPE_CONSTANT:
3018     case REPOKEY_TYPE_CONSTANTID:
3019       break;
3020     case REPOKEY_TYPE_STR:
3021       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
3022       break;
3023     case REPOKEY_TYPE_MD5:
3024       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
3025       break;
3026     case REPOKEY_TYPE_SHA1:
3027       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
3028       break;
3029     case REPOKEY_TYPE_SHA224:
3030       data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
3031       break;
3032     case REPOKEY_TYPE_SHA256:
3033       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
3034       break;
3035     case REPOKEY_TYPE_SHA384:
3036       data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
3037       break;
3038     case REPOKEY_TYPE_SHA512:
3039       data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
3040       break;
3041     case REPOKEY_TYPE_NUM:
3042       if (val & 0x80000000)
3043         {
3044           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
3045           break;
3046         }
3047       /* FALLTHROUGH */
3048     case REPOKEY_TYPE_ID:
3049     case REPOKEY_TYPE_DIR:
3050       data_addid(xd, val);
3051       break;
3052     case REPOKEY_TYPE_BINARY:
3053       {
3054         Id len;
3055         unsigned char *dp = data_read_id(data->attrdata + val, &len);
3056         dp += (unsigned int)len;
3057         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
3058       }
3059       break;
3060     case REPOKEY_TYPE_IDARRAY:
3061       for (ida = data->attriddata + val; *ida; ida++)
3062         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
3063       break;
3064     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3065       for (ida = data->attriddata + val; *ida; ida += 3)
3066         {
3067           data_addid(xd, ida[0]);
3068           data_addid(xd, ida[1]);
3069           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
3070         }
3071       break;
3072     case REPOKEY_TYPE_DIRSTRARRAY:
3073       for (ida = data->attriddata + val; *ida; ida += 2)
3074         {
3075           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
3076           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
3077         }
3078       break;
3079     case REPOKEY_TYPE_FIXARRAY:
3080       {
3081         int num = 0;
3082         schemaid = 0;
3083         for (ida = data->attriddata + val; *ida; ida++)
3084           {
3085             Id *kp;
3086             sp = schema;
3087             kp = data->xattrs[-*ida];
3088             if (!kp)
3089               continue;
3090             num++;
3091             for (;*kp; kp += 2)
3092               *sp++ = *kp;
3093             *sp = 0;
3094             if (!schemaid)
3095               schemaid = repodata_schema2id(data, schema, 1);
3096             else if (schemaid != repodata_schema2id(data, schema, 0))
3097               {
3098                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
3099                 exit(1);
3100               }
3101           }
3102         if (!num)
3103           break;
3104         data_addid(xd, num);
3105         data_addid(xd, schemaid);
3106         for (ida = data->attriddata + val; *ida; ida++)
3107           {
3108             Id *kp = data->xattrs[-*ida];
3109             if (!kp)
3110               continue;
3111             for (;*kp; kp += 2)
3112               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3113           }
3114         break;
3115       }
3116     case REPOKEY_TYPE_FLEXARRAY:
3117       {
3118         int num = 0;
3119         for (ida = data->attriddata + val; *ida; ida++)
3120           num++;
3121         data_addid(xd, num);
3122         for (ida = data->attriddata + val; *ida; ida++)
3123           {
3124             Id *kp = data->xattrs[-*ida];
3125             if (!kp)
3126               {
3127                 data_addid(xd, 0);      /* XXX */
3128                 continue;
3129               }
3130             sp = schema;
3131             for (;*kp; kp += 2)
3132               *sp++ = *kp;
3133             *sp = 0;
3134             schemaid = repodata_schema2id(data, schema, 1);
3135             data_addid(xd, schemaid);
3136             kp = data->xattrs[-*ida];
3137             for (;*kp; kp += 2)
3138               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3139           }
3140         break;
3141       }
3142     default:
3143       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
3144       exit(1);
3145     }
3146   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3147     {
3148       /* put offset/len in incore */
3149       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
3150       oldvincorelen = xd->len - oldvincorelen;
3151       data_addid(newincore, oldvincorelen);
3152     }
3153 }
3154
3155 void
3156 repodata_internalize(Repodata *data)
3157 {
3158   Repokey *key, solvkey;
3159   Id entry, nentry;
3160   Id schemaid, keyid, *schema, *sp, oldschema, *keyp, *seen;
3161   int schemaidx;
3162   unsigned char *dp, *ndp;
3163   int newschema, oldcount;
3164   struct extdata newincore;
3165   struct extdata newvincore;
3166   Id solvkeyid;
3167
3168   if (!data->attrs && !data->xattrs)
3169     return;
3170
3171 #if 0
3172   printf("repodata_internalize %d\n", data->repodataid);
3173   printf("  attr data: %d K\n", data->attrdatalen / 1024);
3174   printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3175 #endif
3176   newvincore.buf = data->vincore;
3177   newvincore.len = data->vincorelen;
3178
3179   /* find the solvables key, create if needed */
3180   memset(&solvkey, 0, sizeof(solvkey));
3181   solvkey.name = REPOSITORY_SOLVABLES;
3182   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3183   solvkey.size = 0;
3184   solvkey.storage = KEY_STORAGE_INCORE;
3185   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3186
3187   schema = solv_malloc2(data->nkeys, sizeof(Id));
3188   seen = solv_malloc2(data->nkeys, sizeof(Id));
3189
3190   /* Merge the data already existing (in data->schemata, ->incoredata and
3191      friends) with the new attributes in data->attrs[].  */
3192   nentry = data->end - data->start;
3193   memset(&newincore, 0, sizeof(newincore));
3194   data_addid(&newincore, 0);    /* start data at offset 1 */
3195
3196   data->mainschema = 0;
3197   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3198
3199   /* join entry data */
3200   /* we start with the meta data, entry -1 */
3201   for (entry = -1; entry < nentry; entry++)
3202     {
3203       memset(seen, 0, data->nkeys * sizeof(Id));
3204       oldschema = 0;
3205       dp = data->incoredata;
3206       if (dp)
3207         {
3208           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3209           dp = data_read_id(dp, &oldschema);
3210         }
3211 #if 0
3212 fprintf(stderr, "oldschema %d\n", oldschema);
3213 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3214 fprintf(stderr, "schemadata %p\n", data->schemadata);
3215 #endif
3216       /* seen: -1: old data  0: skipped  >0: id + 1 */
3217       newschema = 0;
3218       oldcount = 0;
3219       sp = schema;
3220       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3221         {
3222           if (seen[*keyp])
3223             {
3224               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3225               exit(1);
3226             }
3227           seen[*keyp] = -1;
3228           *sp++ = *keyp;
3229           oldcount++;
3230         }
3231       if (entry >= 0)
3232         keyp = data->attrs ? data->attrs[entry] : 0;
3233       else
3234         {
3235           /* strip solvables key */
3236           *sp = 0;
3237           for (sp = keyp = schema; *sp; sp++)
3238             if (*sp != solvkeyid)
3239               *keyp++ = *sp;
3240             else
3241               oldcount--;
3242           sp = keyp;
3243           seen[solvkeyid] = 0;
3244           keyp = data->xattrs ? data->xattrs[1] : 0;
3245         }
3246       if (keyp)
3247         for (; *keyp; keyp += 2)
3248           {
3249             if (!seen[*keyp])
3250               {
3251                 newschema = 1;
3252                 *sp++ = *keyp;
3253               }
3254             seen[*keyp] = keyp[1] + 1;
3255           }
3256       if (entry < 0 && data->end != data->start)
3257         {
3258           *sp++ = solvkeyid;
3259           newschema = 1;
3260         }
3261       *sp = 0;
3262       if (newschema)
3263         /* Ideally we'd like to sort the new schema here, to ensure
3264            schema equality independend of the ordering.  We can't do that
3265            yet.  For once see below (old ids need to come before new ids).
3266            An additional difficulty is that we also need to move
3267            the values with the keys.  */
3268         schemaid = repodata_schema2id(data, schema, 1);
3269       else
3270         schemaid = oldschema;
3271
3272
3273       /* Now create data blob.  We walk through the (possibly new) schema
3274          and either copy over old data, or insert the new.  */
3275       /* XXX Here we rely on the fact that the (new) schema has the form
3276          o1 o2 o3 o4 ... | n1 n2 n3 ...
3277          (oX being the old keyids (possibly overwritten), and nX being
3278           the new keyids).  This rules out sorting the keyids in order
3279          to ensure a small schema count.  */
3280       if (entry >= 0)
3281         data->incoreoffset[entry] = newincore.len;
3282       data_addid(&newincore, schemaid);
3283       if (entry == -1)
3284         {
3285           data->mainschema = schemaid;
3286           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3287         }
3288       /* we don't use a pointer to the schemadata here as repodata_serialize_key
3289        * may call repodata_schema2id() which might realloc our schemadata */
3290       for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
3291         {
3292           if (entry == -1)
3293             data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
3294           if (keyid == solvkeyid)
3295             {
3296               /* add flexarray entry count */
3297               data_addid(&newincore, data->end - data->start);
3298               break;
3299             }
3300           key = data->keys + keyid;
3301 #if 0
3302           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));
3303 #endif
3304           ndp = dp;
3305           if (oldcount)
3306             {
3307               /* Skip the data associated with this old key.  */
3308               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3309                 {
3310                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3311                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3312                 }
3313               else if (key->storage == KEY_STORAGE_INCORE)
3314                 ndp = data_skip_key(data, dp, key);
3315               oldcount--;
3316             }
3317           if (seen[keyid] == -1)
3318             {
3319               /* If this key was an old one _and_ was not overwritten with
3320                  a different value copy over the old value (we skipped it
3321                  above).  */
3322               if (dp != ndp)
3323                 data_addblob(&newincore, dp, ndp - dp);
3324               seen[keyid] = 0;
3325             }
3326           else if (seen[keyid])
3327             {
3328               /* Otherwise we have a new value.  Parse it into the internal form.  */
3329               repodata_serialize_key(data, &newincore, &newvincore, schema, key, seen[keyid] - 1);
3330             }
3331           dp = ndp;
3332         }
3333       if (entry >= 0 && data->attrs)
3334         {
3335           if (data->attrs[entry])
3336             data->attrs[entry] = solv_free(data->attrs[entry]);
3337           if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
3338             {
3339               compact_attrdata(data, entry + 1, nentry);        /* try to free some memory */
3340 #if 0
3341               printf("  attr data: %d K\n", data->attrdatalen / 1024);
3342               printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3343               printf("  incore data: %d K\n", newincore.len / 1024);
3344               printf("  sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
3345               /* malloc_stats(); */
3346 #endif
3347             }
3348         }
3349     }
3350   /* free all xattrs */
3351   for (entry = 0; entry < data->nxattrs; entry++)
3352     if (data->xattrs[entry])
3353       solv_free(data->xattrs[entry]);
3354   data->xattrs = solv_free(data->xattrs);
3355   data->nxattrs = 0;
3356
3357   data->lasthandle = 0;
3358   data->lastkey = 0;
3359   data->lastdatalen = 0;
3360   solv_free(schema);
3361   solv_free(seen);
3362   repodata_free_schemahash(data);
3363
3364   solv_free(data->incoredata);
3365   data->incoredata = newincore.buf;
3366   data->incoredatalen = newincore.len;
3367   data->incoredatafree = 0;
3368
3369   solv_free(data->vincore);
3370   data->vincore = newvincore.buf;
3371   data->vincorelen = newvincore.len;
3372
3373   data->attrs = solv_free(data->attrs);
3374   data->attrdata = solv_free(data->attrdata);
3375   data->attriddata = solv_free(data->attriddata);
3376   data->attrnum64data = solv_free(data->attrnum64data);
3377   data->attrdatalen = 0;
3378   data->attriddatalen = 0;
3379   data->attrnum64datalen = 0;
3380 #if 0
3381   printf("repodata_internalize %d done\n", data->repodataid);
3382   printf("  incore data: %d K\n", data->incoredatalen / 1024);
3383 #endif
3384 }
3385
3386 void
3387 repodata_disable_paging(Repodata *data)
3388 {
3389   if (maybe_load_repodata(data, 0))
3390     {
3391       repopagestore_disable_paging(&data->store);
3392       data->storestate++;
3393     }
3394 }
3395
3396 static void
3397 repodata_load_stub(Repodata *data)
3398 {
3399   Repo *repo = data->repo;
3400   Pool *pool = repo->pool;
3401   int r, i;
3402   struct _Pool_tmpspace oldtmpspace;
3403   Datapos oldpos;
3404
3405   if (!pool->loadcallback)
3406     {
3407       data->state = REPODATA_ERROR;
3408       return;
3409     }
3410   data->state = REPODATA_LOADING;
3411
3412   /* save tmp space and pos */
3413   oldtmpspace = pool->tmpspace;
3414   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3415   oldpos = pool->pos;
3416
3417   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3418
3419   /* restore tmp space and pos */
3420   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3421     solv_free(pool->tmpspace.buf[i]);
3422   pool->tmpspace = oldtmpspace;
3423   if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
3424     memset(&oldpos, 0, sizeof(oldpos));
3425   pool->pos = oldpos;
3426
3427   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3428 }
3429
3430 static inline void
3431 repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
3432 {
3433   Repokey xkey;
3434
3435   xkey.name = keyname;
3436   xkey.type = keytype;
3437   xkey.storage = KEY_STORAGE_INCORE;
3438   xkey.size = 0;
3439   repodata_key2id(data, &xkey, 1);
3440 }
3441
3442 static Repodata *
3443 repodata_add_stub(Repodata **datap)
3444 {
3445   Repodata *data = *datap;
3446   Repo *repo = data->repo;
3447   Id repodataid = data - repo->repodata;
3448   Repodata *sdata = repo_add_repodata(repo, 0);
3449   data = repo->repodata + repodataid;
3450   if (data->end > data->start)
3451     repodata_extend_block(sdata, data->start, data->end - data->start);
3452   sdata->state = REPODATA_STUB;
3453   sdata->loadcallback = repodata_load_stub;
3454   *datap = data;
3455   return sdata;
3456 }
3457
3458 Repodata *
3459 repodata_create_stubs(Repodata *data)
3460 {
3461   Repo *repo = data->repo;
3462   Pool *pool = repo->pool;
3463   Repodata *sdata;
3464   int *stubdataids;
3465   Dataiterator di;
3466   Id xkeyname = 0;
3467   int i, cnt = 0;
3468
3469   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3470   while (dataiterator_step(&di))
3471     if (di.data == data)
3472       cnt++;
3473   dataiterator_free(&di);
3474   if (!cnt)
3475     return data;
3476   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3477   for (i = 0; i < cnt; i++)
3478     {
3479       sdata = repodata_add_stub(&data);
3480       stubdataids[i] = sdata - repo->repodata;
3481     }
3482   i = 0;
3483   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3484   sdata = 0;
3485   while (dataiterator_step(&di))
3486     {
3487       if (di.data != data)
3488         continue;
3489       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3490         {
3491           dataiterator_entersub(&di);
3492           sdata = repo->repodata + stubdataids[i++];
3493           xkeyname = 0;
3494           continue;
3495         }
3496       switch (di.key->type)
3497         {
3498         case REPOKEY_TYPE_ID:
3499           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3500           break;
3501         case REPOKEY_TYPE_CONSTANTID:
3502           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3503           break;
3504         case REPOKEY_TYPE_STR:
3505           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3506           break;
3507         case REPOKEY_TYPE_VOID:
3508           repodata_set_void(sdata, SOLVID_META, di.key->name);
3509           break;
3510         case REPOKEY_TYPE_NUM:
3511           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3512           break;
3513         case_CHKSUM_TYPES:
3514           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3515           break;
3516         case REPOKEY_TYPE_IDARRAY:
3517           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3518           if (di.key->name == REPOSITORY_KEYS)
3519             {
3520               if (!xkeyname)
3521                 {
3522                   if (!di.kv.eof)
3523                     xkeyname = di.kv.id;
3524                 }
3525               else
3526                 {
3527                   repodata_add_stubkey(sdata, xkeyname, di.kv.id);
3528                   xkeyname = 0;
3529                 }
3530             }
3531           break;
3532         default:
3533           break;
3534         }
3535     }
3536   dataiterator_free(&di);
3537   for (i = 0; i < cnt; i++)
3538     repodata_internalize(repo->repodata + stubdataids[i]);
3539   solv_free(stubdataids);
3540   return data;
3541 }
3542
3543 unsigned int
3544 repodata_memused(Repodata *data)
3545 {
3546   return data->incoredatalen + data->vincorelen;
3547 }
3548