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